В этом уроке я покажу вам, как реализовать простое контекстное меню во фрагменте при использовании View Binding для любого элемента (Button, TextView, Layout,…). Я также покажу Вам, как включить/отключить или даже скрыть определенный пункт в контекстном меню.

Давайте создадим новый проект — откройте«Android Studio», выберите «Новый»,и затем « Новый проект».

Теперь выберите «Пустая активность» и нажмите «Далее».

Если вы учатся, но мой совет - изменить их, чтобы привыкнуть к этому.

Выберите «Kotlin» в качестве вашего языка, и я буду работать здесь с минимальным SDK API 29, но вы можете использовать любой, который вам нравится.

Нам нужно создать новый каталог ресурсов, который называется «menu» (если он у вас уже есть, пропустите этот шаг).

Найдите папку «res» и щелкните ее правой кнопкой мыши. Выберите «Создать», а затем «Каталог ресурсов Android».

Выберите «меню» в разделе «Тип ресурса» и нажмите «ОК», ничего не меняя.

Следующий шаг — щелкнуть правой кнопкой мыши вновь созданную папку меню, выбрать «Создать», а затем «Файл ресурсов меню». Назовите его как хотите и нажмите «ОК».

Наконец, давайте создадим или меню! Наш main_menu.xml будет выглядеть так:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/copyItem"
        android:title="Copy" />
    <!--android:icon="@drawable/ic_new_game"-->

    <item
        android:id="@+id/shareItem"
        android:title="Share" />
</menu>

Таким образом, мы будем использовать наш идентификатор, чтобы найти конкретный элемент и использовать его позже в коде, заголовок будет отображаться в нашем меню, и вы можете добавить конкретный значок рядом с вашим заголовком.

Теперь давайте создадим пустой фрагмент, щелкнув правой кнопкой мыши имя нашего пакета, «Новый», «Фрагмент», а затем «Фрагмент (пустой)». .

Назовите его, как хотите, и нажмите «Готово».

Теперь у нас будет «fragment_example.xml» в папке «layout» и «ExampleFragment.kt» в папке нашего пакета.

Теперь давайте отредактируем activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/constraintLayoutActivity"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="com.jskako.contextmenuexample.ExampleFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Итак, теперь у нас будет контейнер фрагментов, в котором будет храниться только что созданный фрагмент примера.

Теперь давайте отредактируем fragment_example.xml.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ExampleFragment">

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/constraintLayoutFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/buttonFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="Test Fragment"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <com.google.android.material.textfield.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@+id/buttonFragment">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/exampleET"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:hint="Edit Text fragment" />
        </com.google.android.material.textfield.TextInputLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

Теперь, когда у нас есть макеты и main_menu, пришло время серьезно поработать. Давайте начнем с реализации привязки в нашем ExampleFragment.kt.

Сначала откройте build.gradle и добавьте этот код в android{}ниже buildFeatures, после добавления кода нажмите «Синхронизировать сейчас”.

buildFeatures {
    viewBinding true
}

Так это будет выглядеть так:

Теперь давайте откроем наш ExampleFragment.kt и отредактируем его:

package com.jskako.contextmenuexample

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.jskako.contextmenuexample.databinding.FragmentExampleBinding

class ExampleFragment : Fragment() {

    private var binding: FragmentExampleBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = FragmentExampleBinding.inflate(inflater, container, false)

        return binding?.root
    }

}

Наконец, долгожданный момент — давайте создадим контекстное меню.

В нашей функции OnCreateView мы зарегистрируем элементы, для которых мы хотим создать контекстное меню, нажав и удерживая их. Я добавлю каждый элемент только для примера, но вы можете использовать любой элемент, который хотите.

binding?.apply { registerForContextMenu(this.buttonFragment) }
binding?.apply { registerForContextMenu(this.exampleET) }
//binding?.apply { registerForContextMenu(this.constraintLayoutFragment) }

Таким образом, наш OnCreateView в ExampleFragment будет выглядеть так:

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    binding = FragmentExampleBinding.inflate(inflater, container, false)

    binding?.apply { registerForContextMenu(this.buttonFragment) }
    binding?.apply { registerForContextMenu(this.exampleET) }

    return binding?.root
}

Теперь нам нужно переопределить функцию onCreateContextMenu и раздуть или создать main_menu, который мы создали ранее.

override fun onCreateContextMenu(
    menu: ContextMenu,
    v: View,
    menuInfo: ContextMenu.ContextMenuInfo?
) {
    super.onCreateContextMenu(menu, v, menuInfo)
    menu.setHeaderTitle("Pick option")
    requireActivity().menuInflater.inflate(R.menu.main_menu, menu)
}

И последний шаг — переопределить onContextItemSelected, где мы указываем, что делать, когда элемент выбран из нашего контекстного меню.

override fun onContextItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        R.id.copyItem -> {
            someCopyFun()
        }
        R.id.shareItem -> {
            someShareFun()
        }
    }
    return super.onContextItemSelected(item)
}

Таким образом, наш ExampleFragment выглядит следующим образом:

package com.jskako.contextmenuexample

import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import com.jskako.contextmenuexample.databinding.FragmentExampleBinding

class ExampleFragment : Fragment() {

    private var binding: FragmentExampleBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = FragmentExampleBinding.inflate(inflater, container, false)

        binding?.apply { registerForContextMenu(this.buttonFragment) }
        binding?.apply { registerForContextMenu(this.exampleET) }

        return binding?.root
    }

    override fun onCreateContextMenu(
        menu: ContextMenu,
        v: View,
        menuInfo: ContextMenu.ContextMenuInfo?
    ) {
        super.onCreateContextMenu(menu, v, menuInfo)
        menu.setHeaderTitle("Pick option")
        requireActivity().menuInflater.inflate(R.menu.main_menu, menu)
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.copyItem -> {
                someCopyFun()
            }
            R.id.shareItem -> {
                someShareFun()
            }
        }
        return super.onContextItemSelected(item)
    }

    private fun someShareFun() {}
    private fun someCopyFun() {}

}

Давайте проверим, работает ли это, создав наше приложение:

И последнее, но не менее важное: давайте посмотрим, как мы можем отключить или скрыть какой-либо элемент из контекстного меню.

Допустим, если наше поле Редактировать текст ("exampleET") пусто, мы хотим отключить элемент Копировать и скрыть Поделиться item — нам нужно добавить несколько строк в наше onCreateContextMenu:

//Disable copy
menu.findItem(R.id.copyItem).isEnabled = !binding?.exampleET?.text.isNullOrEmpty()
//Hide share
menu.findItem(R.id.shareItem).isVisible = !binding?.exampleET?.text.isNullOrEmpty()

Итак, наше onCreateContextMenu будет выглядеть так:

override fun onCreateContextMenu(
    menu: ContextMenu,
    v: View,
    menuInfo: ContextMenu.ContextMenuInfo?
) {
    super.onCreateContextMenu(menu, v, menuInfo)
    menu.setHeaderTitle("Pick option")
    requireActivity().menuInflater.inflate(R.menu.main_menu, menu)

    //Disable copy
    menu.findItem(R.id.copyItem).isEnabled = !binding?.exampleET?.text.isNullOrEmpty()
    //Hide share
    menu.findItem(R.id.shareItem).isVisible = !binding?.exampleET?.text.isNullOrEmpty()
}

В итоге получаем что-то вроде:

Теперь Вы должны получить представление о том, как работать с контекстным меню. Вы можете зарегистрировать контекстное меню на любой понравившийся элемент — даже на макеты.

binding?.apply { registerForContextMenu(this.constraintLayoutFragment) }

Надеюсь, вам понравился туториал и приятного кодинга :)

Найдите этот проект на GitHub: https://github.com/jskako/Android-Kotlin-examples/tree/main/ContextMenu-example-main