android ruunable_sharedPreference

constraintVertical_bias / constraintHorizontal_bias

보통 app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"를 사용하여 레이아웃의 중앙에 위치 하도록 한다.

단, bias를 사용하면 수직/수평의 위치에서 한쪽으로 더 비중을 크게 만들 수 있다. 예를 들어 기본 50%대신 30% bias의 값을 설정하게 되면 왼쪽이 더 짧아지게 만들 수 있다. (그림 5 참고)

<androidx.constraintlayout.widget.ConstraintLayout ...>
    <Button android:id="@+id/button" ...
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

image

AppCompatButton

button의 하위 클래스로 android 버전에서 앱에 최신 테마를 적용하는데 도움을 준다. 따라서 button의 background 및 테마를 적용하게 해준다.

apply() lazy 선언

지연 초기화를 통해 선언을하고 함수 안에 apply 람다식을 활용하여 numberPicker의 값을 설정하는 방법을 사용

private val numberPicker1: NumberPicker by lazy {
    findViewById<NumberPicker>(R.id.numberPicker1)
        .apply {
            minValue = 0
            maxValue = 9
        }
}
private val numberPicker2: NumberPicker by lazy {
    findViewById<NumberPicker>(R.id.numberPicker2)
        .apply {
            minValue = 0
            maxValue = 9
        }
}
private val numberPicker3: NumberPicker by lazy {
    findViewById<NumberPicker>(R.id.numberPicker3)
        .apply {
            minValue = 0
            maxValue = 9
        }
}

Thema

res파일에 values -> themaes의 xml을 통해 앱의 전체 테마를 스타일 적용 할 수 있다.

<style name="Theme.Aoppart2chapter03.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar"/>

style을 추가하여 상단에 액션바가 없는 스타일을 상속받아왔고 해당 Thema를 manifest의 activity 속성의 thema에 name으로 바인딩 해주면 적용된다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fastcampus.aop.part2.chapter03">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Aoppart2chapter03">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.Aoppart2chapter03.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".DiaryActivity"
            android:theme="@style/Theme.Aoppart2chapter03.NoActionBar" />
    </application>

</manifest>

sharedPreference

로컬 DB 저장 또는 파일에 저장하는 방식으로 값을 저장할 수 있으며, sharedPreference는 파일에 값을 저장하고 꺼내서 사용할 수 있게 한다.

//파일에 저장을 하여 가져온다. 이름, 모드(다른곳에서 사용 할 수 있는지)
val passwordPreference = getSharedPreferences("password", Context.MODE_PRIVATE)

위 코드에서 "password"는 name을 의미하며, Map형식처럼 이름을 통해 값을 저장하고 가져올 수 있다. 뒤에 Mode는 해당 sharedPreference의 접근에대한 권한을 설정한다. (private)는 해당 패키지에서만 접근 가능

저장

sharedPreference를 저장하는 방식은 아래와 같다.

passwordPreference.edit {
    putString("password",passwordFromUser)
    commit()
}

putString()에서 첫번째 인자는 name을 넣고 두번째 인자는 값을 넣는다. 그리고 commit()함수를 무조건 실행해줘야 저장이 된다.

passwordPreference.edit(commit = true) {
    putString("password",passwordFromUser)
}

위의 방식처럼 commit=ture를 통해 람다식이 종료되면 무조건 commit을 실행하는 방식을 통해서도 구현이 가능하다.

값 가져오기

값을 가져올때는 name으로 가져오고 default값을 설정해주어야 한다.

passwordPreference.getString("password","000")

Alert

알림 팝업창을 띄우며 다양한 확장함수를 통해 값을 설정할 수 있다. 특히 positiveButton은 두개의 인자를 받는 람다식으로 클릭시 실행할 행동을 추가할 수 있다.

private fun showErrorAlertDialog(){
    AlertDialog.Builder(this)
        .setTitle("실패")
        .setMessage("비밀번호가 잘못되었습니다.")
        .setPositiveButton("확인"){ _, _ -> } //두개의 인자를 받음
        .create()
        .show()
}

runnable

Main UI를 작동하는 쓰레드 이외에 별도의 쓰레드를 생성 Runnable{}인터페이스 람다식을 이용하여 입력한 텍스트를 sharedPreferences에 저장하도록 구현

//별도 쓰레드로 잠깐 멈췄을때 저장
val runnable = Runnable {
    getSharedPreferences("diary",Context.MODE_PRIVATE).edit {
        putString("detail", diaryEditText.text.toString())
    }
    Log.d("DiaryActivity", "SAVE ${diaryEditText.text.toString()}")
}

addTextChangedListener

텍스트가 변경될 때마다 이벤트가 작동하도록하는 리스너이다. diaryEditText라는 EditText에서 텍스트가 변경될때마다 저장되도록 기능 구현.

Handler를 통해 main쓰레드와 runnable쓰레드를 연결하도록한다.’ Handler를 사용하는 이유는 비동기적(Asynchronous)으로 어떤 Job을 처리하기 위해. ※ Activity의 Main thread는 UI thread라고 불리며, UI를 처리할 때 시간이 오래 걸리는 작업을 수행하면 화면이 버벅이거나 ANR(Android Not Responding)과 같은 문제가 발생. Handler는 Looper라는 객체를 통해 동작한다. 1개의 Thread에 1개의 Looper가 있다.

//main UI쓰레드와 별도 쓰레드를 핸들러를 통해서 연결
private val handler = Handler(Looper.getMainLooper())

//텍스트가 바뀔때마다 이벤트 작동
diaryEditText.addTextChangedListener {
    Log.d("DiaryActivity", "TextCahnged::$it")
    handler.removeCallbacks(runnable) //이전에 있는 변화를 우선 지운다
    handler.postDelayed(runnable, 500)
}
diaryEditText 텍스트 변경 -> addTextChangedListener 이벤트 작동 -> main 쓰레드에서 removeCallbacks을 통해 0.5초 이전에 작성된 텍스트는 저장하지 않도록 제거 -> 만약 0.5초가 지나면 저장 (0.5초 지연은 postDelayed()를 통해 진행)