[Project/Android] 오늘의 물가 - #4. Activity 화면 기본 UI 만들기

park_sujeong·2024년 1월 2일
0
post-thumbnail

기획 및 디자인을 보면 화면마다 중복되는 부분이 있다.


바로, 툴바(Toolbar)다.
위 이미지의 빨간색 선 구역을 우린 보통 Toolbar, Appbar로 지칭하는데 이 부분은 대부분의 화면에 들어가기 때문에 이 부분을 기본적으로 구현하고 있는 Activity를 만들것이다. 그리고 이 툴바가 필요한 화면을 만들때는 이 툴바가 구현된 Activity를 상속받을 것이다.





구현 기능


  1. 여러 종류의 툴바 제공

  2. 사이드 네비게이션 제공

  3. 화면 전환 시 애니메이션 제공


위 3가지 기능을 제공하는 Activity를 상속받아 중복되는 코드 줄이는 것이 최종적인 이번 구현의 목적이다.





UI 그리기

기본이 되는 화면의 UI 구조는 아래와 같다.


메뉴 버튼을 누르면 왼쪽에서 메뉴 화면이 슬라이드되면서 나온다. 이 화면이 필요없으면 DrawerLayout이랑 NavigationView는 제외하고 CoordinatorLayout부터 만들면 된다.


activity_base.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/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".component.BaseActivity">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/background"
            app:elevation="0dp"
            android:fitsSystemWindows="true">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/toolbarLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                app:collapsedTitleTextColor="@color/black"
                app:contentScrim="@color/background"
                app:expandedTitleTextColor="@color/white"
                app:expandedTitleTextAppearance="@style/TextAppearance.Design.CollapsingToolbar.Expanded.Shadow"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/backgroundImageView"
                    android:layout_width="match_parent"
                    android:layout_height="300dp"
                    android:background="@color/background"
                    android:fitsSystemWindows="true"
                    android:scaleType="centerCrop"
                    android:visibility="gone" />

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:elevation="0dp"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    app:contentInsetStart="0dp"
                    app:contentInsetEnd="0dp"
                    app:layout_collapseMode="pin" />

            </com.google.android.material.appbar.CollapsingToolbarLayout>


        </com.google.android.material.appbar.AppBarLayout>
        <ScrollView
            android:id="@+id/contentLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@string/activity_container"
            app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/background"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/drawer_hearder"
        app:menu="@menu/drawer" />

</androidx.drawerlayout.widget.DrawerLayout>





기본 Activity 만들기

각 코드에 주석 참고


BaseActivity.kt

// 툴바 타입
enum class ToolbarType {
    MENU,
    BACK,
    HOME,
}

// 애니메이션 타입
enum class TransitionMode {
    NONE,
    HORIZON,
    VERTICAL
}

open class BaseActivity(
    private val toolbarType: ToolbarType,
    private val transitionMode: TransitionMode = TransitionMode.NONE,
) : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

	// activity_base의 dataBinding
    protected lateinit var baseBinding: ActivityBaseBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        baseBinding = ActivityBaseBinding.inflate(layoutInflater)
        setContentView(baseBinding.root)
        setSupportActionBar(baseBinding.toolbar)
        setToolbar()
       
       // navigationView에 리스너 추가
       baseBinding.navigationView.setNavigationItemSelectedListener(this)

		// 애니메이션 모드에 따라 애니메이션 설정 (화면 나타날때)
        when (transitionMode) {
            TransitionMode.HORIZON -> overridePendingTransition(R.anim.horizon_enter, R.anim.none)
            TransitionMode.VERTICAL -> overridePendingTransition(R.anim.vertical_enter, R.anim.none)
            else -> {}
        }

    }

    override fun finish() {
        super.finish()

		// 애니메이션 모드에 따라 애니메이션 설정 (화면 사라질 때)
        when (transitionMode) {
            TransitionMode.HORIZON -> overridePendingTransition(R.anim.none, R.anim.horizon_exit)
            TransitionMode.VERTICAL -> overridePendingTransition(R.anim.none, R.anim.vertical_exit)
            else -> {}
        }
    }

    override fun onBackPressed() {
    
    	// navigationView가 나와있으면 onBackPressed()가 아닌 navigationView 닫기
        if (baseBinding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
            baseBinding.drawerLayout.closeDrawers()
        } else {
            super.onBackPressed()

            if (isFinishing) {
                when (transitionMode) {
                    TransitionMode.HORIZON -> overridePendingTransition(
                        R.anim.none,
                        R.anim.horizon_exit
                    )
                    TransitionMode.VERTICAL -> overridePendingTransition(
                        R.anim.none,
                        R.anim.vertical_exit
                    )
                    else -> Unit
                }
            }
        }
    }

    override fun onDestroy() {
    	// navigationView 리스너 제거
        baseBinding.navigationView.setNavigationItemSelectedListener(null)
        super.onDestroy()
    }

	// Toolbar의 아이템 클릭 별 수행 코드
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_search -> {
                actionMenuSearch()
            }

            R.id.action_favorite -> {
                actionMenuFavorite()
            }

            R.id.action_home -> {
                actionMenuHome()
            }

            android.R.id.home -> {
                actionHome()
            }
        }
        return super.onOptionsItemSelected(item)
    }
    
    // 툴바 타입에 따라 메뉴 아이템 변경
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        when (toolbarType) {
            ToolbarType.MENU -> menuInflater.inflate(R.menu.menu_with_search, menu)
            ToolbarType.BACK -> menuInflater.inflate(R.menu.back_with_search, menu)
            ToolbarType.HOME -> menuInflater.inflate(R.menu.back_with_home, menu)
        }
        return true
    }
    
    private fun setToolbar() {
    // 툴바 타입에 따라 왼쪽 상단 이미지 변경
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.setBackgroundDrawable(
            ColorDrawable(
                ContextCompat.getColor(
                    applicationContext,
                    R.color.background
                )
            )
        )

        when (toolbarType) {
            ToolbarType.MENU -> supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu)
            ToolbarType.BACK -> supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_back)
            ToolbarType.HOME -> supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_back)
        }

    }

	// 상단에 이미지 띄우는게 필요할 때 사용 (상속받는 Activity에서 호출)
    fun setImageView(url: String) {
        if (url.isNotEmpty()) {
            Glide.with(baseBinding.root)
                .load(url)
                .centerCrop()
                .error(R.drawable.no_picture)
                .into(baseBinding.backgroundImageView)
        } else {
            baseBinding.backgroundImageView.setImageResource(R.drawable.no_picture)
        }
    }

	// 상단에 타이틀이 필요한 경우 사용 (상속받는 Activity에서 호출)
    fun setTitle(title: String) {
        baseBinding.toolbarLayout.title = title
        baseBinding.backgroundImageView.isVisible = true
        supportActionBar?.setBackgroundDrawable(
            ColorDrawable(
                ContextCompat.getColor(
                    applicationContext,
                    android.R.color.transparent
                )
            )
        )
    }

    private fun actionMenuSearch() {
        Toast.makeText(this, "검색하기", Toast.LENGTH_SHORT).show()
    }
    
    private fun actionMenuFavorite() {
        Toast.makeText(this, "즐겨찾기", Toast.LENGTH_SHORT).show()
    }
    
    private fun actionMenuHome() {
        Toast.makeText(this, "호오옴", Toast.LENGTH_SHORT).show()
    }
    
    
    private fun actionHome() {
        Toast.makeText(this, "왼쪽 상단 버튼", Toast.LENGTH_SHORT).show()
    }

	// navigationView의 item 클릭 시 수행 코드
    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.menuItem1 -> Toast.makeText(this, "item1 is clicked", Toast.LENGTH_SHORT).show()
            R.id.menuItem2 -> Toast.makeText(this, "item2 is clicked", Toast.LENGTH_SHORT).show()
            R.id.menuItem3 -> Toast.makeText(this, "item3 is clicked", Toast.LENGTH_SHORT).show()
        }
        return false
    }
}





상속받는 Activity 만들기

이제 기본 Activity를 상속받는 Activity를 만들어보자.


activity_item.xml

<?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">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/itemImageView"
            android:layout_width="150dp"
            android:layout_height="150dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="20dp"
            android:background="@drawable/background_image" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

ItemActivity.kt

//  BaseActivity를 상속하고 인자로 툴바 타입과 애니메이션 방향 타입 전달
class ItemActivity: BaseActivity(ToolbarType.HOME, TransitionMode.HORIZON)  {
   
    private lateinit var binding: ActivityItemBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        binding = ActivityItemBinding.inflate(layoutInflater)
        
        // BaseActivity의 contentLayout (ScrollView)에 추가
        baseBinding.contentLayout.addView(binding.root)

    }
}





결과물

이후로 좀 더 개발이 진행된 상태다.





profile
Android Developer

0개의 댓글