Evgenii Legotckoi
Evgenii Legotckoi2 марта 2018 г. 2:53

Android. Java vs Qt QML - Урок 004. Создание меню в Action Bar с подменю

Итак, продолжаем реализовывать одинаковый функционал в приложениях под Android из двух принципиально различных миров. А именно на традиционной Java и менее распространённом Qt QML C++.

На этот раз реализуем меню в ActionBar активити. Отмечу, что ActionBar для варианта с QML придётся писать самостоятельно, поскольку такого элемента в Qt QML нет, но он реализуется через компонент ToolBar. Поскольку этот момент был описан в уроке про Hello World , то в данном уроке тщательно останавливаться на нём не будем, только вынесем ActionBar в отдельный QML файл, сделав его отдельным типом QML и добавим в него иконку меню, по нажатию на которую будем вызывать меню.

Меню будет следующую структуру:

  • Information
  • First
  • Second
  • About

В центре окна приложения должен быть текст, который будет заменяться другим текстом при нажатии следующие пункты меню

  • First
  • Second
  • About

Внешний вид меню на Java

Первый уровень

Второй уровень

Внешний вид меню на Qt QML

Первый уровень

Второй уровень

Реализация на JAVA

menu.xml

Для вёрстки меню необходимо создать XML файл, который будет располагаться в ресурсном каталоге menu. Так и назовём его menu.xml

Здесь всё достаточно просто, есть небольшая вёрстка с названиями пунктов меню, вложенными элементами и id, по которым можно будет обратиться к данным пунктам из Java кода.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/informationItem"
        android:title="Information">
        <menu>
            <item
                android:id="@+id/firstInfoItem"
                android:title="First" />
            <item
                android:id="@+id/secondInfoItem"
                android:title="Second" />
        </menu>
    </item>
    <item
        android:id="@+id/aboutItem"
        android:title="About" />
</menu>

activity_main.xml

Далее идёт вёрстка самой активити, которая в данном случае создана по умолчанию.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.evileg.javamenu.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java

А теперь нужно инициализировать все компоненты меню и навешать на них обработчик события клика.

package com.evileg.javamenu;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends Activity {

    // Объявим объекты всех пунктов меню
    MenuItem aboutItem;
    MenuItem firstInfoItem;
    MenuItem secondInfoItem;

    // А также текстового поля, в которое будем устанавливать текст
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView); // найдём данное текстовое поле в ресурсах
    }

    // Создадим обработчики пунктов меню
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);

        // Найдём все элементы пунктов меню
        aboutItem = menu.findItem(R.id.aboutItem);
        firstInfoItem = menu.findItem(R.id.firstInfoItem);
        secondInfoItem = menu.findItem(R.id.secondInfoItem);

        // Создадим обработчик кликов по пунктам меню
        MenuItem.OnMenuItemClickListener onMenuClickListener = new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.aboutItem:
                        textView.setText("About Menu Item is pressed");
                        break;
                    case R.id.firstInfoItem:
                        textView.setText("First Info Item is pressed");
                        break;
                    case R.id.secondInfoItem:
                        textView.setText("Second Info Item is pressed");
                        break;
                }
                return true;
            }
        };

        // Установим этот обработчик на пункты меню
        aboutItem.setOnMenuItemClickListener(onMenuClickListener);
        firstInfoItem.setOnMenuItemClickListener(onMenuClickListener);
        secondInfoItem.setOnMenuItemClickListener(onMenuClickListener);

        return true;
    }
}

Реализация на Qt QML

ActionBar.qml

Я создал специальный тип ActionBar в отдельном файле QML, чтобы выделить его из основного файл main.qml. Это позволит отделить этот код от остальной полезной части кода, которая должна показать различие реализаций меню на Java и на Qt QML. А также этот ActionBar будет использоваться в следующих статьях.

Обратите внимание на сигнал menuClicked , этот сигнал будет вызываться в ActionBar, когда пользователь нажмёт пальцем на MouseArea, которая отвечает за клик по нашей кастомной кнопке меню. По этомму сигналу будет открыто меню, которое расположено в файле main.qml

import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.1

ToolBar {
    id: root
    height: 48

    signal menuClicked() // Сигнал, который сообщит о клике по кнопке меню

    Rectangle {
        anchors.fill: parent
        color: "#080808"

        Image {
            id: ico
            height: 36
            width: 36
            anchors {
                left: parent.left
                leftMargin: 10
                top: parent.top
                topMargin: 6
            }
            source: "qrc:/icons/ic_launcher.png"
        }


        Text {
            anchors {
                verticalCenter: parent.verticalCenter
                left: ico.right
                leftMargin: 4
            }

            text: qsTr("QMLMenu")
            font.pixelSize: 18
            color: "white"
        }

        // Начало реализации кнопки меню
        Rectangle {
            width: height
            color: menuArea.pressed ? "#55ffffff" : "transparent"
            anchors {
                top: parent.top
                right: parent.right
                bottom: parent.bottom
            }

            Image {
                id: menuIcon
                height: 36
                width: 36
                anchors {
                    centerIn: parent
                }

                source: "qrc:/icons/menu.png"
            }

            MouseArea {
                id: menuArea
                anchors.fill: parent
                onClicked: root.menuClicked()
            }
        }
        // конец реализации кнопки меню
    }
}

main.qml

А здесь будет помещён код того, ради чего и писался данный урок. Показать, как реализовать само меню.

import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.1

ApplicationWindow {
    id: root
    visible: true
    width: 360
    height: 520
    title: qsTr("QML Menu")

    // Добавляем ActionBar в окно приложения
    header: ActionBar {
        onMenuClicked: actionBarMenu.open() // Открываем меню

        // Добавляем меню
        Menu {
            id: actionBarMenu
            // С указанием расположения, чтобы Menu выскакивало в том же месте,
            // что и в варианте с Java
            x: root.width
            y: 48
            Menu {
                title: qsTr("Information")
                Action {
                    text: qsTr("First")
                    // Обработка клика по меню
                    onTriggered: label.text = qsTr("First Info Item is pressed")
                }
                Action {
                    text: qsTr("Second")
                    onTriggered: label.text = qsTr("Second Info Item is pressed")
                }
            }
            Action {
                text: qsTr("About")
                onTriggered: label.text = qsTr("About Menu Item is pressed")
            }
        }
    }

    Label {
        id: label
        anchors.centerIn: parent
        text: qsTr("Hello World!")
        font.pixelSize: 14
    }
}

Заключение

Конечно, имеется некоторая разница в реализации меню по умолчанию для Java и для Qt QML, например наличие заголовка подменю. Теоретически в QML это можно реализовать с помощью пустого Action, который будет иметь title, будет немного кастомизирован и не будет ничего делать при клике, но нужно ли это?

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Михаиллл
  • 9 июля 2019 г. 9:33
  • (ред.)

Добрый день.
Как в такое меню добавить картинку и просто лэйблы с текстом?
И скажите пожалуйста как связаны ActionBar.qml и main.qml?

Михаиллл
  • 10 июля 2019 г. 4:08
  • (ред.)

Превый файл скопировал, в другой qml вставил этот код, но меню не появляется

         ActionBar {
            onMenuClicked: actionBarMenu.open() // Открываем меню

            // Добавляем меню
            Menu {
                id: actionBarMenu
                // С указанием расположения, чтобы Menu выскакивало в том же месте,
                // что и в варианте с Java
                x: root.width
                y: 48
                Menu {
                    title: qsTr("Information")
                    Action {
                        text: qsTr("First")
                        // Обработка клика по меню
                        //onTriggered: label.text = qsTr("First Info Item is pressed")
                    }
                    Action {
                        text: qsTr("Second")
                        //onTriggered: label.text = qsTr("Second Info Item is pressed")
                    }
                }
                Action {
                    text: qsTr("About")
                    //onTriggered: label.text = qsTr("About Menu Item is pressed")
                }
            }
        }

Вот так работает

    Button {

        x: 5
        y: 5
        id:  menuButton
        text:  "Menu"
        onClicked:  myMenu.open()

    }

    Menu {
        id:  myMenu
        x: 5
        y: menuButton.height

        MenuItem {
            text:  "Мои данные"
            onClicked: console.log("Мои данные")
        }
        MenuItem {
            text:  "Чат"
            onClicked: console.log("Чат")
        }
        MenuItem {
            text:  "Видео"
            onClicked: console.log("Видео")
        }
        MenuItem {
            text:  "Контакты"
            onClicked: console.log("Контакты")
        }

    }
Evgenii Legotckoi
  • 11 июля 2019 г. 4:27
  • (ред.)

У объектов MenuItem есть свойства iconName и iconSource. В iconSource можно добавить иконку из ресурсов в проекте. Можете воспользоваться этим свойством и тогда у вас будут в пунктах меню и иконки и текст.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
e
  • ehot
  • 31 марта 2024 г. 14:29

C++ - Тест 003. Условия и циклы

  • Результат:78баллов,
  • Очки рейтинга2
B

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
B

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
Последние комментарии
k
kmssr8 февраля 2024 г. 18:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 1:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 10:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 8:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 декабря 2023 г. 21:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
a
a_vlasov14 апреля 2024 г. 6:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 2:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 4:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 февраля 2023 г. 4:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 11:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Следите за нами в социальных сетях