[Android/Flutter 교육] 23일차

MSU·2024년 1월 29일

Android-Flutter

목록 보기
24/85
post-thumbnail

내가 작성한 EX09 코드에서는 학생별 정보를 각각의 리스트로 담아서 정보를 각각 전달했는데
강사님의 경우 info클래스를 작성하여 객체를 전달하는 방식으로 작성하셨다.
점수 통계를 내는 과정도 내가 작성한 코드는 리스트를 통째로 전달해서 통계뷰에서 계산을 하고 보여주는 방식으로 작성했는데, 강사님의 경우 메인에서 먼저 계산을 한 후에 계산값을 통계뷰에 전달하여 출력하는 방식으로 작성하셨다.
강사님의 코드에서는 학생정보 입력 시 예외처리를 따로 안하셨는데 예외처리 하는 부분도 복습 겸 구현이 필요하다.
Parcelable 복습이 꼭 필요하다.

리사이클러뷰 항목의 position은 adapterPosition으로 찾을 수 있음
studentList[adapterPosition] 이런식으로

// 항목을 터치했을 때의 이벤트
                this.rowMainBinding.root.setOnClickListener {
                    // ShowInfoActivity를 실행한다.
                    val newIntent = Intent(this@MainActivity, ShowInfoActivity::class.java)

                    // 선택한 항목 번째의 객체를 Intent에 담아준다.
                    newIntent.putExtra("obj", studentList[adapterPosition])

                    showInfoActivityLauncher.launch(newIntent)
                }

EX09

리사이클러뷰 항목을 클릭할 때 애니메이션 효과 부여

레이아웃의 배경속성을 selectableItemBackground로 설정하면 된다.
android:background="?android:attr/selectableItemBackground"

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/selectableItemBackground"
    android:orientation="vertical"
    android:padding="10dp">

객체 전달하기

  • Intent를 통해 객체를 전달할때는 객체 직렬화를 해야 하는데 안드로이드는 Parcelable 인터페이스를 사용한다.
  • Parcelable 인터페이스는 전달 받은 쪽에서 객체를 복원할 때 필요한 정보를 가진 부분을 의미한다.
  • A activity에서 B activity로 객체를 전달할 때
    그냥 객체만 전달하면 B에서는 A에서 생성한 객체의 주소값을 전달받기 때문에 (종속성)
    A가 종료되면 A에서 생성한 객체도 사라지면서 B에서 해당 객체를 사용할 수 없기 때문
    따라서 주소값을 전달하는게 아니라 객체가 갖고 있는 프로퍼티만 전달하여 A에서 생성된 객체와 독립된 새로운 객체를 생성해줌
  • 이러한 종속성 문제 우려가 있는 객체는 전부 안드로이드 OS가 갖고 있음, Intent 객체도 activity가 아니라 안드로이드 OS가 갖고 있음
// InfoClass.kt
class InfoClass(var name: String?, var grade:Int, var kor:Int, var eng:Int, var math:Int) : Parcelable {

}
  • 클래스에 Parcelable 인터페이스를 구현하면 아래와 같이 안드로이드스튜디오 자동완성으로 오버라이딩 함수를 불러올 수 있다.
// InfoClass.kt

package kr.co.lion.ex09_activity

import android.os.Parcel
import android.os.Parcelable


class InfoClass(var name: String?, var grade:Int, var kor:Int, var eng:Int, var math:Int) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readInt(),
        parcel.readInt(),
        parcel.readInt(),
        parcel.readInt()
    ) {
    }

    // 객체를 다른 실행단위로 보낼때 호출되는 메서드
    // 안드로이드 OS에서 첫 번째 매개변수로 parcel 객체가 전달된다.
    // 이 객체는 다른 실행 단위로 전달되는 객체이다.
    // parcel 객체는 객체가 가지고 있는 프로퍼티의 값을 저장해준다.
    // 이 값은 다른 실행단위로 전달된다.
    // 이 메서드는 putExtra 메서드를 호출할 때 호출된다.
    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeInt(grade)
        parcel.writeInt(kor)
        parcel.writeInt(eng)
        parcel.writeInt(math)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<InfoClass> {
        // 이 메서드는 parcel을 전달받은 새로운 실행단위에서 호출되는 메서드
        // 이 메서에서는 사용하고자 하는 객체를 생성한다.
        // 매개 변수에는 parcel 객체가 전달된다.
        // parcel 객체에 저장되어 있는 값들을 추출하여 새롭게 생성한 객체의
        // 프로퍼티에 저장해주는 작업을 한다.
        override fun createFromParcel(parcel: Parcel): InfoClass {
            return InfoClass(parcel)
        }

        override fun newArray(size: Int): Array<InfoClass?> {
            return arrayOfNulls(size)
        }
    }

}

// MainActivity.kt
...
    // 초기 데이터 셋팅
    fun initData(){


        // InputActivity 등록
        val contract1 = ActivityResultContracts.StartActivityForResult()
        inputActivityLauncher = registerForActivityResult(contract1){
            if(it.resultCode == RESULT_OK){
                if(it.data != null){
                    // 객체를 추출한다.
                    // createFromParcel 메서드가 호출되고 반환하는 객체를 반환해준다.
                    val info1 = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
                        it.data!!.getParcelableExtra("obj", InfoClass::class.java)
                    } else {
                        it.data!!.getParcelableExtra<InfoClass>("obj")
                    }

//                    Log.d("test1234", "${info1?.name}")
//                    Log.d("test1234", "${info1?.grade}")
//                    Log.d("test1234", "${info1?.kor}")
//                    Log.d("test1234", "${info1?.eng}")
//                    Log.d("test1234", "${info1?.math}")

                    // 리스트에 객체를 담는다.
                    studentList.add(info1!!)
                    // 리사이클러 뷰를 갱신한다.
                    activityMainBinding.recyclerViewItem.adapter?.notifyDataSetChanged()
                }
            }
        }        
	...
    }
...

Toolbar

  • 이전에는 ActionBar를 사용했으나 지금은 쓰지 않고 Toolbar를 사용

TopAppBar

홈버튼, 타이틀, 메뉴버튼 3개로 구성되어있음

Menu Item을 Menu에 배치할 때 ComponentTree가 아니라 미리보기 화면에 드래그를 하는 것을 권장함
ComponentTree에 드래그하여 배치할 경우 코드상으로 layout_width와 layout_height가 자동생성됨 -> MenuItem에는 필요없는 속성임

  • id : 각 메뉴를 구분하기 위한 이름

  • title : 메뉴에 표시되는 문자열

  • showAsAction : 메뉴 항목을 툴바에 배치할 것인지를 설정한다.

    • always : 항상 툴바에 배치한다.

      아이콘이 있을 경우

      아이콘이 없을 경우

    • ifRoom : 공간이 허락할 경우 툴바에 배치한다.

    • never : 툴바에 배치하지 않는다.

    • withText : 아이콘이 설정되어 있을 경우 아이콘이 보여지고 공간이 허락되면 title에 설정된 문자열도 보인다.

메뉴아이템 안에 메뉴를 또 구성하여 하위메뉴를 만들 수 있다.

package kr.co.lion.android27_toolbar

import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kr.co.lion.android27_toolbar.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        
        activityMainBinding.apply { 
            toolbar.apply { 
                // 타이틀
                title = "툴바 입니다"
                // 타이틀 문자열 색상
                setTitleTextColor(Color.WHITE)
                // res/menu/main_menu.xml 파일을 이용해 툴바의 메뉴를 생성한다.
                // id : 각 메뉴를 구분하기 위한 이름
                // title : 메뉴에 표시되는 문자열
                // showAsAction : 메뉴 항목을 툴바에 배치할 것인지를 설정한다.
                //         always : 항상 툴바에 배치한다.
                //         ifRoom : 공간이 허락할 경우 툴바에 배치한다.
                //         never : 툴바에 배치하지 않는다.
                //         withText : 아이콘이 설정되어 있을 경우 아이콘이 보여지고 공간이 허락되면 title에 설정된 문자열도 보인다.
                //         collapseActionView는 사용하지 않음
                inflateMenu(R.menu.main_menu)
                // 메뉴를 선택하면 동작하는 리스너
                // 매개변수에는 사용자가 선택한 메뉴 항목의 객체가 전달된다.
                setOnMenuItemClickListener {
                    // 선택한 메뉴의 id로 분기한다.
                    when(it.itemId){
                        R.id.menuItem1 -> textView.text = "메뉴 1을 선택했습니다."
                        R.id.menuItem2 -> textView.text = "메뉴 2를 선택했습니다."
                        R.id.menuItem31 -> textView.text = "메뉴 3-1을 선택했습니다."
                        R.id.menuItem32 -> textView.text = "메뉴 3-2를 선택했습니다."
                    }
                    true
                }
            }
        }
    }
}

좌측상단 아이콘 설정

// SecondActivity.kt
package kr.co.lion.android27_toolbar

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kr.co.lion.android27_toolbar.databinding.ActivityMainBinding
import kr.co.lion.android27_toolbar.databinding.ActivitySecondBinding

class SecondActivity : AppCompatActivity() {

    lateinit var activitySecondBinding: ActivitySecondBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activitySecondBinding = ActivitySecondBinding.inflate(layoutInflater)
        setContentView(activitySecondBinding.root)

        activitySecondBinding.apply {
            toolbarSecond.apply {
                title = "SecondActivity"

                // 좌측 상단의 홈 버튼 아이콘을 설정한다.
                setNavigationIcon(R.drawable.arrow_back_24px)

            }
        }
    }
}



※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스 
profile
안드로이드공부

0개의 댓글