[Android] 제트팩을 이용해 화면 구성하기

이유빈·2023년 7월 31일
0

Android

목록 보기
2/3
post-thumbnail

제트팩

구글에서는 안드로이드 앱을 개발하는 데 필요한 다양한 라이브러리 모음을 제트팩이라는 이름으로 제공한다. androidx로 시작하는 패키지명을 사용한다.

제트팩의 목적은 다음 3가지이다.

  • 앱을 개발하는 데 필요한 권장 아키텍처를 제공한다.
  • API 레벨의 호환성 문제를 해결한다.
  • 플랫폼 API에서 제공하지 않는 다양한 기능을 제공한다.

appcompat 라이브러리

androidx 라이브러리에서 가장 많이 사용하는 appcompat 라이브러리는 안드로이드 앱의 화면을 구성하는 액티비티를 만들며 API 레벨의 호환성 문제를 해결해 준다.

appcompat 라이브러리를 이용해서 액티비티를 만들 때는 플랫폼 API의 Activity가 아니라 다음처럼 appcompat의 AppCompatActivity 클래스를 상속받아 작성한다.

import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity(){
}

액션바

액티비티의 구성 요소인 액션바는 화면 위쪽에 타이틀 문자열이 출력되는 영역을 의미한다. 액티비티가 출력되는 전체 창은 액션바와 콘텐츠 영역으로 구분된다.

  1. 액션바 색상 설정
    테마 스타일은 res/values 디렉터리에 있는 themes.xml 파일에 선언되어 있다.
    colorPrimary는 액션바와 버튼의 배경색으로 사용하고 colorSecondary는 활성 상태를 표현한다. statusBarColor는 상태바의 배경색으로 사용한다.
  2. 액션바 숨기기 설정
    테마를 만들 때 Theme.MaterialComponents.DayNight.NoActionBar를 상속받으면 액션바가 나오지 않는다.
  3. 업 버튼 설정
    매니페스트 파일에서 설정하는 방법과 액티비티 코드로 설정하는 방법이 있다.
  4. 액션 뷰 이용
    액션 뷰는 액션바에서 특별한 기능을 제공하며 대표적으로 androidx.appcompat.widget.SearchView가 있다. 서치 뷰는 액션바에서 검색 기능을 제공한다. actionViewClass 속성에 이용할 액션 뷰 클래스를 등록하는 것만으로도 액션바에 검색 버튼이 생기고 버튼을 클릭하면 검색어를 입력받는 뷰가 나온다.
<item android:id="@+id/menu_search"
	android:title="search"
    app:showAsAction="always"
    app:actionViewClass="androidx.appcompat.widget.SearchView"/>

프래그먼트

프래그먼트는 텍스트 뷰나 버튼처럼 액티비티 화면을 구성하는 뷰인데, 그 자체만으로는 화면에 아무것도 출력되지 않는다. 프래그먼트가 다른 뷰와 다른 점은 액티비티처럼 동작한다는 것이다.

프래그먼트 구현

프래그먼트는 뷰이지만 그 자체로는 화면에 아무것도 출력되지 않는다. 따라서 먼저 프래그먼트 화면을 구성하는 레이아웃 XML 파일을 작성해야 한다.


리사이클러 뷰

여러 가지 항목을 나열할 때 리사이클러 뷰를 이용한다.
다음과 같은 구성 요소를 이용해야 한다.

  • ViewHolder(필수): 항목에 필요한 뷰 객체를 가진다.
  • Adapter(필수): 항목을 구성한다.
  • LayoutManager(필수): 항목을 배치한다.
  • ItemDecoration(옵션): 항목을 꾸민다.

뷰 페이저2

뷰 페이저는 스와이프 이벤트로 화면을 전환할 때 사용한다.

먼저 레이아웃 XML 파일에 뷰 페이저2를 추가한다.
리사이클러 뷰 어댑터를 이용한다.
화면 3개를 뷰 페이저2로 제공하는 어댑터라면 이 어덥터를 뷰 페이저2에 적용하면 된다.

binding.viewpager.adapter = MyPagerAdapter(datas)

드로어 레이아웃

드로어 레이아웃은 액티비티 화면에 보이지 않던 내용이 왼쪽이나 오른쪽에 손가락의 움직임에 따라 밀려 나오는 기능을 한다.

최상위에는 <androidx.drawerlayout.widget.DrawerLayout> 태그를, 그 아래에는 과 태그를 선언한다. 태그가 2개인 것이 중요하다. 이렇게 XML 설정만 해도 첫 번째 태그에 해당하는 화면이 알아서 나오고 사용자가 화면 끝을 밀면 두 번째 태그에 해당하는 화면이 나타난다.


제트팩을 이용해 화면 구성하기

그럼 이를 종합적으로 활용해 화면을 구성해보겠다.

먼저 프래그먼트를 3개 생성해준다. 각각 OneFragment, TwoFragment, ThreeFragment라는 이름으로 작성해준다.

다음은 OneFragment.kt 파일이다.

class MyViewHolder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root)

class MyAdapter(val datas: MutableList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
  override fun getItemCount(): Int {
      return datas.size;
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
      MyViewHolder(ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent,
          false))

  override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
      val binding = (holder as MyViewHolder).binding
      binding.itemData.text = datas[position]
  }

}

class MyDecoration(val context: Context): RecyclerView.ItemDecoration() {
  override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
      super.onDrawOver(c, parent, state)

      val width = parent.width
      val height = parent.height

      val dr: Drawable? = ResourcesCompat.getDrawable(context.resources, com.google.android.material.R.drawable.abc_ab_share_pack_mtrl_alpha ,null)
      val drWidth = dr?.intrinsicWidth
      val drHeight = dr?.intrinsicHeight

      val left = width/2 - drWidth?.div(2) as Int
      val top = height/2 - drHeight?.div(2) as Int

      c.drawBitmap(
          BitmapFactory.decodeResource(context.resources, com.google.android.material.R.drawable.abc_ab_share_pack_mtrl_alpha),
          left.toFloat(),
          top.toFloat(),
          null)
  }

  override fun getItemOffsets(
      outRect: Rect,
      view: View,
      parent: RecyclerView,
      state: RecyclerView.State
  ) {
      super.getItemOffsets(outRect, view, parent, state)
      val index = parent.getChildAdapterPosition(view)+1
      if(index % 3 == 0)
          outRect.set(10, 10, 10, 60)
      else
          outRect.set(10, 10, 10, 0)

      view.setBackgroundColor(Color.parseColor("#28A0FF"))
      ViewCompat.setElevation(view, 20.0f)
  }
}
class OneFragment : Fragment() {

  override fun onCreateView(
      inflater: LayoutInflater, container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View? {
      val binding = FragmentOneBinding.inflate(inflater, container, false)

      val datas = mutableListOf<String>()
      for(i in 14..23){
          datas.add("SSU CSE $i 학번")
      }

      val adapter = MyAdapter(datas)
      val layoutManager = LinearLayoutManager(activity)
      binding.recyclerView.layoutManager = layoutManager
      binding.recyclerView.adapter = adapter
      binding.recyclerView.addItemDecoration(MyDecoration(activity as Context))
      return binding.root

  }

}

다음은 MainActivity.kt 파일이다.

class MainActivity : AppCompatActivity() {

  lateinit var toggle: ActionBarDrawerToggle

  class MyFragmentPagerAdapter(activity: FragmentActivity): FragmentStateAdapter(activity){
      val fragments: List<Fragment>
      init {
          fragments = listOf(OneFragment(), TwoFragment(), ThreeFragment())
      }

      override fun getItemCount(): Int = fragments.size
      override fun createFragment(position: Int): Fragment = fragments[position]
  }

  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      val binding = ActivityMainBinding.inflate(layoutInflater)
      setContentView(binding.root)
      setSupportActionBar(binding.toolbar)
      toggle = ActionBarDrawerToggle(this, binding.drawer, R.string.drawer_opened,
          R.string.drawer_closed)
      supportActionBar?.setDisplayHomeAsUpEnabled(true)
      toggle.syncState()

      val adapter = MyFragmentPagerAdapter(this)
      binding.viewpager.adapter = adapter
  }

  override fun onCreateOptionsMenu(menu: Menu?): Boolean {
      val inflater = menuInflater
      inflater.inflate(R.menu.menu_main, menu)

      val menuItem = menu?.findItem(R.id.menu_search)
      val searchView = menuItem?.actionView as SearchView
      searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
          override fun onQueryTextChange(newText: String?): Boolean {
              return true
          }

          override fun onQueryTextSubmit(query: String?): Boolean {
              Log.d("kkang", "search text: $query")
              return true
          }
      })
      return true
  }

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
      if(toggle.onOptionsItemSelected(item)){
          return true
      }
      return super.onOptionsItemSelected(item)
  }
}

다음은 실행 결과이다. 모두 스와이프 해서 나타나는 화면들이다.



profile
Computer Science && Engineering

1개의 댓글

comment-user-thumbnail
2023년 7월 31일

좋은 정보 감사합니다

답글 달기