Android/UI

[UI] Context Menu 사용법

잘할수있을거야 2021. 9. 18. 17:27

View를 롱 터치하면 컨텍스트 메뉴가 생성되고 View근처에 뜨게 된다.

컨텍스트 메뉴를 사용하기 위해 View.onCreateContextMenuListener인터페이스를 구현( onCreateContextMenu메서드)

뷰에 setOnCreateContextMenuListener(구현객체)로 등록

롱 터치시 구현객체의 onCreateContextMenu메서드가 호출되어 컨텍스트 메뉴가 호출된다.

Activity의 onContextItemSelected를 오버라이딩 ( 컨텍스트 메뉴의 메뉴아이템이 선택되면 호출된다 )


1. 관련된 View의 메서드들과 인터페이스

 

  • setOnCreateContextMenuListener 

setOnClickListener를 통해 리스너를 등록하고 뷰가 클릭이 되면

View.OnClickListener구현객체의 onClick에 구현해놓은 내용이 호출되었다.

 

마찬가지로 setOnCreateContextMenuListener도 뷰가 롱 터치되면

View.OnCreateContextMenuListener구현객체의 onCreateContextMenu가 호출되어 컨텍스트 메뉴를 생성한다.


  • View.OnCreateContextMenuListener 인터페이스의 onCreateContextMenu메서드

뷰에 컨텍스트메뉴 리스너를 설정하고 롱터치가 발생하면 onCreateContextMenu메서드가 호출되는데 

여기서 적절한 컨텍스트 메뉴를 생성해주면 된다.

abstract fun onCreateContextMenu( menu: ContextMenu!, v: View!, menuInfo: ContextMenu.ContextMenuInfo! ): Unit menu- 현재 만들어질 컨텍스트 메뉴
v- 어떤 뷰를 위해 컨텍스트 메뉴를 생성하고 있는지( 롱 터치된 뷰)
menuInfo- 컨텍스트 메뉴의 메뉴 아이템 추가 정보. v에 따라 다르다.

onCreateContextMenu{
//뷰에 따라 생성해야할 컨텍스트 메뉴가 다르다면
    if(뷰1){
    	//메뉴1.xml 인플레이션
    }
    if(뷰2){
    	//메뉴2.xml 인플레이션
    }
}

Activity또한 View.OnCreateContextMenuListener를 구현했기 떄문에

Activity에서 onCreateContextMenu메서드를 오버라이딩한후 

뷰.setOnCreateContextMenuListener( this //액티비티) 로 사용할 수 있다.


2. 관련된 Activity의 메서드들

  • onCreateContextMenu 메서드(롱 터치시 onCreateContextMenu를 통해 컨텍스트 메뉴가 만들어지고 화면 출력됨)

Activity는 View.OnCreateContextMenuListener를 구현하고 있다.

다음과 같은 방식으로 View에 컨텍스트 메뉴 리스너를 등록할 수 있다.

 

MainActivity

//MainActivity

//리스너 메서드 구현
override fun onCreateContextMenu(...){}

onStart메서드 {
	뷰1.setOnCreateContextListener(this)
    	뷰2.setOnCreateContextListener(this)
}

onStop메서드{
	뷰1.setOnCreateContextListener(null)
    	뷰2.setOnCreateContextListener(null)
}

뷰1, 뷰2는 이제 롱 클릭이 되면 액티비티에 구현된 onCreateContextMenu가 호출되어 컨텍스트 메뉴가 생성된다.

 

RecyclerView의 itemView에 컨텍스트 메뉴를 사용하려면 

ViewHolder 생성자 내에서 따로 리스너를 만들어 사용할 수 있다.

 


  • registerForContextMenu(view), unregisterForContextMenu(view)

바로 위의 뷰1.setOnCreateContextListener(this)는 registerForContextMenu(뷰1)과 동일하다.

 

Activity.java

Activity의 구현체를 리스너로 설정할 수도있으며, 직접 객체를 만들어서 리스너를 설정할 수도있다.

 

View가 롱 터치되고 설정된 리스너의 콜백 메서드 onCreateContextMenu를 통해 컨텍스트 메뉴를 만들게 된다.

작업이 끝나면 컨텍스트 메뉴가 화면에 출력되고 사용자가 아이템을 선택하면 onContextItemSelected메서드가 호출된다.

 


  • onContextItemSelected(item: MenuItem) : Boolean

만들어진 컨텍스트 메뉴안의 메뉴 아이템이 선택되면 호출된다.


  • onContextMenuClosed(menu: Menu): Unit

컨텍스트 메뉴가 화면에 보이는 상태에서 메뉴 아이템이 선택되지 않고 back버튼, 또는 컨텍스트 메뉴 바깥의 공간을 터치하면 호출된다.


테스트 

 

context_menu1.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/context_menu_item1"
        android:title="컨텍스트 메뉴 아이템1" />
    <item
        android:id="@+id/context_menu_item2"
        android:title="컨텍스트 메뉴 아이템2" />
    <item
        android:id="@+id/context_menu_item3"
        android:title="컨텍스트 메뉴 아이템3" />
    <item
        android:id="@+id/context_menu_item4"
        android:title="컨텍스트 메뉴 아이템4" />
    <item
        android:id="@+id/context_menu_item5"
        android:title="컨텍스트 메뉴 아이템5"/>

</menu>


context_menu2.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/context_menu2_item1"
        android:title="컨텍스트 메뉴2 아이템1" />
    <item
        android:id="@+id/context_menu2_item2"
        android:title="컨텍스트 메뉴2 아이템2" />
    <item
        android:id="@+id/context_menu2_item3"
        android:title="컨텍스트 메뉴2 아이템3" />
    <item
        android:id="@+id/context_menu2_item4"
        android:title="컨텍스트 메뉴2 아이템4" />
    <item
        android:id="@+id/context_menu2_item5"
        android:title="컨텍스트 메뉴2 아이템5"/>

</menu>


activity_test.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TestActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#e2e2e2"
        android:gravity="center"
        android:text="타입1"
        app:layout_constraintBottom_toTopOf="@+id/textView2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#e2e2e2"
        android:gravity="center"
        android:text="타입2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

TestActivity.kt

package com.source.ui.contextmenutext

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.TextView
import android.widget.Toast

class TestActivity : AppCompatActivity() {

    lateinit private var textView: TextView
    lateinit private var textView2: TextView

    private fun log(str: String) { //로그 출력
        Log.d("ContextMenu", str)
    }

    private fun toast(str: String) { //토스트 출력
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        log("onCreate")
        setContentView(R.layout.activity_test)
        textView = findViewById<TextView>(R.id.textView)
        textView2 = findViewById<TextView>(R.id.textView2)
    }

    override fun onStart() { //리스너 등록 -
        super.onStart()
        log("onStart")

        registerForContextMenu(textView) //Activity method
        //= textView.setOnCreateContextMenuListener(this)
        registerForContextMenu(textView2) //Activity method
        //= textView2.setOnCreateContextMenuListener(this)
    }

    override fun onStop() { //리스너 해제
        super.onStop()
        log("onStop")

        unregisterForContextMenu(textView)
        //= textView.setOnCreateContextMenuListener(null)
        unregisterForContextMenu(textView2)
        //= textView2.setOnCreateContextMenuListener(null)
    }

    //Activity implements View.OnCreateContextMenuListener
    override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) { //롱 터치시 호출됨-> 컨텍스트 메뉴 생성
        super.onCreateContextMenu(menu, v, menuInfo)
        log("onCreateContextMenu")

        val menuInflater = this.getMenuInflater()
        if (v != null) { //View에 따라 분기
            if (v.id == R.id.textView) {
                menuInflater.inflate(R.menu.context_menu1, menu)
            }
            if (v.id == R.id.textView2) {
                menuInflater.inflate(R.menu.context_menu2, menu)
            }
        } else {
            Toast.makeText(this, "onCreateContextMenu View parameter is null", Toast.LENGTH_SHORT).show()
        }
    }
	
    //어떠한 컨텍스트 메뉴인지 알수 없어 적절히 분기해야한다.
    override fun onContextItemSelected(item: MenuItem): Boolean {
        log("onContextItemSelected")

        when (item.itemId) {

            //컨텍스트 메뉴가 context_menu1.xml일때
            R.id.context_menu_item1 -> {
                toast("1")
                return true
            }
            R.id.context_menu_item2 -> {
                toast("2")
                return true
            }
            R.id.context_menu_item3 -> {
                toast("3")
                return true
            }
            R.id.context_menu_item4 -> {
                toast("4")
                return true
            }
            R.id.context_menu_item5 -> {
                toast("5")
                return true
            }

            //컨텍스트 메뉴가 context_menu2.xml일때
            R.id.context_menu2_item1 -> {
                toast("one")
                return true
            }
            R.id.context_menu2_item2 -> {
                toast("two")
                return true
            }
            R.id.context_menu2_item3 -> {
                toast("three")
                return true
            }
            R.id.context_menu2_item4 -> {
                toast("four")
                return true
            }
            R.id.context_menu2_item5 -> {
                toast("five")
                return true
            }
            else -> return false
        }
        //boolean Return false to allow normal context menu processing to proceed, true to consume it here.
    }
    
}