반응형

 

햄버거 버튼 동작이벤트를 넣어보자.

 

1. GNB UI 생성

MainActivity 로 이어질 Home

별도 Activity 로 이어질 버튼을 넣어보겠다

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <LinearLayout
        android:id="@+id/ll_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginRight="0dp"
        android:layout_marginLeft="0dp"
        android:orientation="vertical"
        android:background="@color/white">

        <LinearLayout
            android:id="@+id/ll_item_1"
            android:layout_width="match_parent"
            android:layout_height="53dp"
            android:gravity="center_vertical"
            android:paddingLeft="40dp"
            android:paddingRight="20dp"
            android:layout_marginBottom="4dp"
            android:background="@color/home_menu_bg_light_gray"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_item_1"
                android:layout_width="wrap_content"
                android:layout_height="53dp"
                android:layout_gravity="center_vertical"
                android:paddingLeft="0dp"
                android:gravity="center_vertical"
                android:text="Home"
                android:textSize="@dimen/sp_17"
                android:maxLines="1"
                android:textColor="@color/orange"
                android:textStyle="bold" />
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
            </View>
            <ImageView
                android:id="@+id/iv_orange_arrow1"
                android:layout_width="7dp"
                android:layout_height="12dp"
                android:layout_marginRight="0dp"
                android:background="@drawable/btn_detail_org"/>
        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_item_2"
            android:layout_width="match_parent"
            android:layout_height="53dp"
            android:gravity="center_vertical"
            android:paddingLeft="40dp"
            android:paddingRight="20dp"
            android:layout_marginBottom="4dp"
            android:background="@color/home_menu_bg_light_gray"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_item_2"
                android:layout_width="wrap_content"
                android:layout_height="53dp"
                android:layout_gravity="center_vertical"
                android:paddingLeft="0dp"
                android:gravity="center_vertical"
                android:text="기본 Recycler View"
                android:textSize="@dimen/sp_17"
                android:maxLines="1"
                android:textColor="@color/orange"
                android:textStyle="bold" />
            <View
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
            </View>
            <ImageView
                android:id="@+id/iv_orange_arrow2"
                android:layout_width="7dp"
                android:layout_height="12dp"
                android:layout_marginRight="0dp"
                android:background="@drawable/btn_detail_org"/>
        </LinearLayout>

    </LinearLayout>
</layout>

 

나는 이런식으로 추가해봤다.

메뉴가 몇개안되기때문에 LinearLayout을 사용했음!!

 

2. MainActivity 에 GNB 영역 추가

다른 액티비티에서는 사용하지않고 Main 에 들어왔을때만 보이면 되기때문에 MainActivity 에만 추가하자

<com.example.basic_mobile.ui.TitleBar
        android:id="@+id/inc_titlebar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_height"
        app:layout_constraintTop_toTopOf="parent"/>

////추가////
<com.google.android.material.navigation.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:orientation="vertical"
    android:visibility="gone"
    tools:ignore="MissingConstraints">

    <com.example.basic_mobile.ui.CustomDrawerMenu
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</com.google.android.material.navigation.NavigationView>

 

 

 

그런데!! 이렇게하면 햄버거버튼 위에 위치하며 버튼이랑 겹쳐진다

각 요소들을 나열해놓기만 하고 줄을 제대로 세우지 않았기때문에 이런식으로 먹혀버린다...

 

 

LinearLayout 으로 서열을 제대로 알려주자

 

<?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=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.example.basic_mobile.ui.TitleBar
            android:id="@+id/inc_titlebar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/title_height" />

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:orientation="vertical"
            android:visibility="gone">

            <com.example.basic_mobile.ui.CustomDrawerMenu
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </com.google.android.material.navigation.NavigationView>
        <Button
            android:id="@+id/btn_recycler"
            android:text="기본 Recycler View"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/btn_epoxy"
            android:text="epoxy View"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@+id/btn_recycler"/>
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

 

 

이런식으로 ConstraintLayout -> LinearLayout -> 요소 배치 의 방식으로 설정하면 된다.

 

자 이제 보여줄 화면을 세팅해놨다면, 햄버거버튼 클릭시에만 NavigationView 가 visible 되도록 해보자

 

3. GNB 클릭 이벤트 설정

MainActivity 에서 햄버거버튼을 클릭했을때,

메뉴영역이 노출되어있으면 visibility = gone, 미노출되어있으면 visible 상태로 만들어보자.

 

하지만 햄버거버튼, 뒤로가기, 타이틀 로고 이미지클릭시 실행되는 동작들이 Activity 별로 달라야할때가 있다.

즉 include 된 페이지의 이벤트를 부모 페이지에게 알려줘야할때 쓰는 방법을 사용해보겠다

 

1) TitleBar 상수 추가

Title bar 에서 어떤 동작을 수행했는지 알려주기 위해서 상수를 추가했다

class TitleBar : FrameLayout {
    companion object {
        const val BUTTON_NAME_BACK = 1 //뒤로가기
        const val BUTTON_NAME_HOME_MENU = 2 //햄버거버튼
        const val BUTTON_NAME_HOME = 3 //타이틀
    }
    ...
}

 

뒤로가기, 햄버거버튼, 타이틀을 클릭했을때 각 상수값을 부모 페이지로 보내도록 하겠다

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            ///이곳에서 상수값을 보내고 싶음
        }
    }
...
}

 

자 어떻게 보내야할까?

 

내가 택한 방법으로는 DataChangeListener를 이용하는것이다.

 

리스너를 하나 만들고 부모에서 해당리스너를 오버라이딩 시켜서 사용하는 방법이다. 차근차근 해보자

 

2) DataChangeListener 추가

TitleBar 하단에 interface로 리스너를 하나 만들어준다.

그리고 그 안에 메소드를 하나 추가해서 파라미터로 상수값을 제공받는다

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...

    private var binding: IncTitlebarBinding? = null
    private var dataChangeListener: DataChangeListener? = null
    
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_MENU,"")
        }
    }
...

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

햄버거버튼을 클릭했을 때, 해당 리스너의 onChanged 메소드로 상수값을 전달한다

그렇다면 이것을 부모가 쓰게끔 컨트롤가능한 메소드를 하나 추가해준다

 

3) 부모페이지 전용 메소드 추가

class TitleBar : FrameLayout {
    object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_MENU = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }
...

    private var binding: IncTitlebarBinding? = null
    private var dataChangeListener: DataChangeListener? = null
    
    private fun initView() {

        //햄버거버튼 클릭 이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_MENU,"")
        }
    }
...

	//부모 사용 전용 메소드
    fun setOnClickBackBtnListener(listener: (Int, String) -> Unit) {
        this.dataChangeListener = object : DataChangeListener {
            override fun onChanged(index: Int, value: String) {
                listener(index, value)
            }
        }
    }

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

이렇게 메소드를 만들어서 뚫어줘야 부모에서 사욯할 수 있다.

 

전체 코드는 이렇다

class TitleBar : FrameLayout {
    companion object {
        const val BUTTON_NAME_BACK = 1
        const val BUTTON_NAME_HOME_RIGHT_SIDE = 2
        const val BUTTON_NAME_HOME = 3
        const val BUTTON_NAME_CLOSE = 4
    }

    constructor(context: Context) : super(context) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        inflateView()
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        inflateView()
        initView()
    }

    private var binding: IncTitlebarBinding? = null
    private  var dataChangeListener: DataChangeListener? = null

    private fun inflateView() {
        binding = IncTitlebarBinding.inflate(LayoutInflater.from(context))
        addView(binding?.root)
    }

    private fun initView() {

        //클릭이벤트
        binding?.ivMenu?.setOnClickListener() {
            if(dataChangeListener != null)
                this.dataChangeListener?.onChanged(BUTTON_NAME_HOME_RIGHT_SIDE,"")
        }
    }

    /**
     * set title (TEXT)
     */
    fun setTitle(title:String) {
        if (title.isNullOrEmpty()) {
            binding?.tvTitle?.hide()
        } else {
            binding?.tvTitle?.show()
            binding?.tvTitle?.text = title
        }
    }

    fun showMenuIcon(b: Boolean) {
        if (b) {
            binding?.ivMenu?.show()
        } else {
            binding?.ivMenu?.hide()
        }
    }

    fun setOnClickBackBtnListener(listener: (Int, String) -> Unit) {
        this.dataChangeListener = object : DataChangeListener {
            override fun onChanged(index: Int, value: String) {
                listener(index, value)
            }
        }
    }

    interface DataChangeListener {
        fun onChanged(index: Int,  value : String);
    }

}

 

 

4.  Main Activity 에서 리스너 사용

setTitleBar 부분에서 아래와같이 해당 메소드를 이용할 수 있다

class MainActivity : AppCompatActivity() {

    private var binding: ActivityMainBinding? = null
    lateinit var drawerLayout: DrawerLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding!!.root)
        setTitleBar()
    }

    private fun setTitleBar () {
        binding?.incTitlebar?.setTitle("MAIN")
        binding?.incTitlebar?.showMenuIcon(true)
        binding?.incTitlebar?.setOnClickBackBtnListener { i, s ->
            when (i) {
                TitleBar.BUTTON_NAME_HOME -> {} //로고 클릭 event
                TitleBar.BUTTON_NAME_BACK -> {} //뒤로가기 event
                TitleBar.BUTTON_NAME_HOME_MENU -> { //햄버거버튼 event
                    if (binding?.navView?.isVisible == true) {
                        binding?.navView?.hide()
                    } else {
                        binding?.navView?.show()
                    }

                }
            }
        }
    }

}

 

뭐 모든 페이지가 같은 동작을 수행한다면 굳이 이럴필요는 없지만 

아닌경우가 많기때문에.. 해당방법을 사용했다.

 

중심적으로 봐야할점은 부모로 이벤트를 보내는 방법!! 나중에 fragment 에서 리스트클릭하거나 다양한 이벤트를 보낼때 이러한 방식을 사용하게된다.

 

완성본!

 

 

 

 

 

728x90

+ Recent posts