본문 바로가기

프로젝트형 IoT 서비스 개발 4회차/3. 게이트웨이 디바이스 제어

[Day73] 2022-05-10(화) Android12 - Menu, Tab, View Pager - 김서연 강사님

728x90

[1] Menu

  1. 메뉴 생성 예제

    1) menu resource 생성

res 에서 오른쪽 버튼 클릭하여 Android Resource Directory 클릭
menu resource 디렉토리 생성
생성한 menu 리소스 디렉토리 오른쪽 버튼 클릭하여, Menu Resource File 생성

menu_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/menu_drawer_1"
        android:icon="@android:drawable/ic_menu_directions"
        android:title="내가 본 레시피"/>
    <item
        android:id="@+id/menu_drawer_2"
        android:icon="@android:drawable/ic_menu_myplaces"
        android:title="스크랩한 레시피"/>
    <item
        android:id="@+id/menu_drawer_3"
        android:icon="@android:drawable/ic_menu_camera"
        android:title="리뷰한 레시피"/>
    <item
        android:id="@+id/menu_drawer_4"
        android:icon="@android:drawable/ic_menu_help"
        android:title="레시피노트"/>
    <item
        android:id="@+id/menu_drawer_5"
        android:icon="@android:drawable/ic_menu_today"
        android:title="레시피 공유"/>


</menu>

    2) Drawer Layout 생성 (메인 화면)

empty activity 추가하고, layout파일의 layout을 DrawerLayout으로 변경

activity_drawer_test.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/root_drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".drawer.DrawerTest">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/drawertxt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Navigation View Test"
            android:textSize="30sp"
            android:textStyle="bold"/>
    </LinearLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/main_drawer_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/menu_drawer"
        android:layout_gravity="start"/>
</androidx.drawerlayout.widget.DrawerLayout>

navigation_header.xml

(activity_drawer_test.xml 의 NavigationView의 속성 중 app:headerLayout에 들어가는 레이아웃)

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="250dp"
    android:background="@drawable/lee"
    android:gravity="bottom"
    android:paddingLeft="16dp"
    android:paddingBottom="16dp"
    android:text="Hello Android"
    android:textColor="@android:color/black"
    android:textSize="20dp"
    android:textStyle="bold" />

    3) themes.xml 리소스파일에 item 추가

themes.xml

themes.xml 에서 item 추가

   4) 메뉴에 액션 추가

DrawerTest.kt

package com.example.fragment.drawer

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.drawerlayout.widget.DrawerLayout
import com.example.fragment.R
import kotlinx.android.synthetic.main.activity_drawer_test.*

class DrawerTest : AppCompatActivity() {
    var drawerLayout:DrawerLayout? = null
    var actionBarDrawerToggle:ActionBarDrawerToggle? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_drawer_test)
        // 액션바에 출력되는 타이틀을 출력되지 않도록 정의
        supportActionBar?.setDisplayShowTitleEnabled(false)
        //root_drawer
        // 문자열은 화면에 출력할 문자열과 관련없이 drawer가 열린 상태, 닫힌 상태냐를 표현하기 위한 문자열
        actionBarDrawerToggle = ActionBarDrawerToggle(this, root_drawer, R.string.drawer_open, R.string.drawer_close)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        actionBarDrawerToggle?.syncState()
        main_drawer_view.setNavigationItemSelectedListener { item ->
            val id = item.itemId
            if(id == R.id.menu_drawer_1){
                showToast("내가 본 레시피 메뉴가 선택됨")
            }
            false
        }
    }

    // 실제로 NavigationView가 열리거나 닫히도록 하려면 onOptionsItemSelected 메소드를 반드시 오버라이딩 해야한다.
    // 이 함수를 사용하는 이유는 ActionBarDrawerToggle 아이콘 클릭하는 것이 메뉴 이벤트로 처리되기 때문에
    // 이벤트 로직에 벗어나도록 코드 구현
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        if(actionBarDrawerToggle?.onOptionsItemSelected(item)==true){
            showToast("눌림")
            return false    // 일반 메뉴처리를 계속 하려면 false를 반환하고 사용하려면 true 반환
        }
        return super.onOptionsItemSelected(item)
    }

    fun showToast(msg:String){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }
}

[2] Tab

  1. Tab 생성 예제

CoordinatorLayout으로 변경
AppBarLayout 추가

activity_tab_test.xml

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

    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            app:tabMode="scrollable">

            <com.google.android.material.tabs.TabItem
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="가입" />

            <com.google.android.material.tabs.TabItem
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="로그인" />

            <com.google.android.material.tabs.TabItem
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="마이페이지" />
        </com.google.android.material.tabs.TabLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
        <LinearLayout
            android:id="@+id/linear1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"/>
    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

TabTest.kt

package com.example.fragment.tab

import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import com.example.fragment.R
import com.example.fragment.fragment.LoginFragment
import com.example.fragment.fragment.MyPageFragment
import com.example.fragment.fragment.RegisterFragment
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.activity_tab_test.*

class TabTest : AppCompatActivity() {
    var view1 = RegisterFragment()
    var view2 = LoginFragment()
    var view3 = MyPageFragment()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tab_test)
        // 탭 추가
        tabs.addTab(tabs.newTab().setText("설정"))
        //tabs.setTabTextColors(Color.RED,Color.BLUE)   // 탭 텍스트 색 변경

        // 처음 실행할 때 보여줄 프레그먼트 지정
        supportFragmentManager.beginTransaction().replace(R.id.linear1,view1).commit()

        // 탭에 이벤트 연결
        tabs.addOnTabSelectedListener(object:TabLayout.OnTabSelectedListener{
            // 탭이 선택될 때 호출되는 메소드
            override fun onTabSelected(tab: TabLayout.Tab?) {
                val position = tab?.position    // 탭의 순서를 받아오기
                var fragment: Fragment? = null
                when(position){
                    0 -> fragment = view1
                    1 -> fragment = view2
                    2 -> fragment = view3
                }
                // 탭을 선택할 때 정의된 프레그먼트 객체가 show되도록 연결
                supportFragmentManager.beginTransaction().replace(R.id.linear1,fragment!!).commit()
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {

            }

            override fun onTabReselected(tab: TabLayout.Tab?) {

            }

        })
    }
}

- 나머지 코드는 파일로 대체

LoginFragment.kt
0.00MB
MyPageFragment.kt
0.00MB
RegisterFragment.kt
0.00MB
login.xml
0.00MB
mypage.xml
0.00MB
register.xml
0.00MB

[3] View Pager

  1. ViewPager 구버전(?) 사용 예제

activity_view_pager_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".viewpager.ViewPagerTestActivity">

    <TextView
        android:id="@+id/pager_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Info"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

    <view
        android:id="@+id/pager1"
        class="androidx.viewpager.widget.ViewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

ViewPagerTest.kt

package com.example.fragment.viewpager

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.example.fragment.R
import kotlinx.android.synthetic.main.activity_view_pager_test.*

/*
    화면 전환을 위해서 ViewPager를 사용하는 경우(ListView와 동일)
    1. ViewPager에 담을 View를 데이터(ViewPager에 담을 View객체 - View, Fragment)를 정의
    2. Adapter 커스터마이징 - ViewPager에 보여주는 방식이나 ViewPager에 담을 뷰들이 달라지고 각각 동작할 수 있도록 해야 하므로 Adapter를 상속 받아서 작업
        => PagerAdapter
    3. ViewPager에 Adapter 연결

*/

class ViewPagerTestActivity : AppCompatActivity() {
    val viewlist = ArrayList<View>()    // ViewPager에 담을 뷰들을 보관할 자료구조
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_pager_test)

        // 1. ViewPager에 담을 View객체를 생성
        // => root를 레이아웃을 지정하면 그 레이아웃에 가서 추가된다.
        val view1 = layoutInflater.inflate(R.layout.register, null)
        val view2 = layoutInflater.inflate(R.layout.login, null)
        val view3 = layoutInflater.inflate(R.layout.mypage, null)
        viewlist.add(view1)
        viewlist.add(view2)
        viewlist.add(view3)
        // 2. Adapter를 커스터마이징(Adapter를 상속받아서 구현) - 익명으로 구현
        val my_viewpager_adapter = object:PagerAdapter(){
            // ViewPager를 통해 보여줄 뷰의 갯수를 리턴
            override fun getCount(): Int {
                return viewlist.size
            }
            // ViewPager에 보여줄 뷰를 등록 - 뷰페이저에서 뷰가 전환될 때마다 보여줄 뷰를 만들어서 리턴
            override fun instantiateItem(container: ViewGroup, position: Int): Any {
                pager1.addView(viewlist[position])
                return viewlist[position]
            }
            // instantiateItem에서 만든 뷰를 실제 사용할 것인지 여부를 결정 - 매개변수로 전달된 뷰들끼리 비교
            // 첫번째 매개변수로 전달된 뷰객체와 instantiateItem 메소드를 호출한 후 리턴되는 객체를 비교
            // `object` => 코틀린에서 키워드를 식별자로 사용할 때 ``로 묶어준다.
            override fun isViewFromObject(view: View, `object`: Any): Boolean {
                return view == `object`
            }

            // ViewPager에서 View가 사라질 때 뷰를 제거하는 작업을 수행
            override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
                pager1.removeView(`object` as View)
            }

        }// end Adapter
        // 3. ViewPager에 어댑터 연결
        pager1.adapter = my_viewpager_adapter

        // ViewPager에 이벤트 연결하기
        val listener1 = object: ViewPager.OnPageChangeListener{
            // 페이지 스크롤이 끝났을 때 호출되는 메소드
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                pager_text.text = "${position}번재 뷰가 선택됨"
            }
            // 페이지를 선택했을 때 호출되는 메소드
            override fun onPageSelected(position: Int) {

            }
            // 페이지 상태가 변경되었을 때 호출되는 메소드
            override fun onPageScrollStateChanged(state: Int) {

            }

        }
        pager1.addOnPageChangeListener(listener1)
    }
}

  2. ViewPager2 사용 예제

    - ViewPager 보다 간단하게 작성 가능 - 동작은 동일

activity_view_pager2_test.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".viewpager.ViewPager2TestActivity">
    <TextView
        android:id="@+id/pager_text2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Info"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager2"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

ViewPager2TestActivity.kt

package com.example.fragment.viewpager

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.fragment.R
import com.example.fragment.fragment.LoginFragment
import com.example.fragment.fragment.MyPageFragment
import com.example.fragment.fragment.RegisterFragment
import kotlinx.android.synthetic.main.activity_view_pager2_test.*

// ViewPager2를 이용할 때 FragmentActivity를 상속받아 작업하기
class ViewPager2TestActivity : FragmentActivity() {
    var view1 = RegisterFragment()
    var view2 = LoginFragment()
    var view3 = MyPageFragment()
    val fragmentlist = ArrayList<Fragment>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_pager2_test)
        // 1. ViewPager2에 연결될 프레그먼트 준비
        fragmentlist.add(view1)
        fragmentlist.add(view2)
        fragmentlist.add(view3)

        // 2. ViewPager2도 어댑터가 있어야 한다. - FragmentStateAdapter
        // FragmentStateAdapter가 커스터마이징될 때 FragmentActivity를 요구하기 때문에 액티비티는 FragmentActivity 하위로 작업
        val my_viewpager2_adapter = object:FragmentStateAdapter(this){
            // viewpager2를 통해 보여줄 프레그먼트 갯수
            override fun getItemCount(): Int {
                return fragmentlist.size
            }
            // 보여줄 프레그먼트만 리턴
            override fun createFragment(position: Int): Fragment {
                return fragmentlist[position]
            }

        }
        // 3. 뷰페이저2에 어댑터 연결
        pager2.adapter = my_viewpager2_adapter
    }
}

 

[4] View Pager와 Tab 연결

  1. ViewPager2와 Tab 연결 예제

    1) Fragment

subfragment_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/subfragment_txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="fragment"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"/>
</LinearLayout>

SubFragment.kt

package com.example.fragment.tab

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.fragment.R
import kotlinx.android.synthetic.main.subfragment_view.*

class SubFragment: Fragment {
    lateinit var title:String   // title 변수를 선언하고 바로 초기화하지 않고 뒤에서 초기화하겠다는 의미
    constructor(title:String){
        this.title = title
    }
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.subfragment_view,null,false)
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        subfragment_txt.text = title
    }
}

    2) 메인 Activity

activity_tab_test2.xml

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

    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="scrollable">

        </com.google.android.material.tabs.TabLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/tab_viewpager2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

TabTest2.kt

package com.example.fragment.tab

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.fragment.R
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.activity_tab_test2.*

class TabTest2 : FragmentActivity() {
    var fragmentlist = ArrayList<Fragment>()

    // 탭 문자열을 담을 ArrayList
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tab_test2)
        // 동일한 프레그먼트를 만들어서 작업하는 거라면 만들어질 프레그먼트를 담을 프레그먼트 객체를 준비하고 작업
        for(i in 0..9){
            val sub = SubFragment("${i}번째 프레그먼트")
            fragmentlist.add(sub)
        }

        val myadapter = object:FragmentStateAdapter(this){
            override fun getItemCount(): Int {
                return fragmentlist.size
            }

            override fun createFragment(position: Int): Fragment {
                return fragmentlist[position]
            }

        }
        tab_viewpager2.adapter = myadapter

        // 탭과 뷰페이저를 연결 - 연결할 탭과 뷰페이저를 이용해서 TabLayoutMediator 객체를 정의
        TabLayoutMediator(tabs2, tab_viewpager2){tab, position->
            tab.text = "탭$position" // 탭의 이름
        }.attach()
    }
}

 

- 끝 -

728x90