75일차(2)/Android App(31) : Shared Preferences(1)
새 모듈 생성 - Empty Activity
- 코틀린으로 생성
MainActivity.kt
package com.example.step13sharedpref
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
/*
App에서 문자열을 영구 저장하는 방법(영구 저장이란 앱을 종료하고 다시 시작해도 불러올 수 있는 문자열)
1. 파일 입출력을 이용해서 저장
2. android 내장 DataBase를 이용해서 저장 => QLite DataBase (안드로이드에 기본으로 내장된 프로그램)
3. SharedPreference를 이용해서 저장 (느리지만 간단히 저장하고 불러올 수 있다)
내부적으로 xml 문서를 만들어서 문자열을 저장하고 불러온다.
저장된 문자열을 boolean, int, double, String type 으로 변환해서 불러올 수 있다.
*/
class MainActivity : AppCompatActivity(), View.OnClickListener { //extends AppCompatActivity implements View.OnClickListener
/*
java에서는 field 를 선언만 하면 자동으로 null로 초기화된다.
kotlin 에서는 null이 가능한 field 를 만들어서 명시적으로 넣어주어야 한다.
*/
var editText:EditText?=null
// onCreate() 메소드 재정의
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// res/layout/activity_main.xml 문서를 전개해서 화면 구성하기
setContentView(R.layout.activity_main)
//EditText 객체의 참조값 얻어오기
editText=findViewById<EditText>(R.id.editText)
//Button 객체의 참조값 얻어오기
//val saveBtn=findViewById<Button>(R.id.saveBtn)
val saveBtn:Button=findViewById(R.id.saveBtn)
saveBtn.setOnClickListener(this)
val readBtn=findViewById<Button>(R.id.readBtn)
/*
readBtn.setOnClickListener(object:View.OnClickListener {
override fun onClick(v: View?) {
}
})
*/
//위의 코드를 간략히 표현하면 아래와 같다.
readBtn.setOnClickListener {
val pref:SharedPreferences = getSharedPreferences("info", Context.MODE_PRIVATE)
// "msg" 라는 키값으로 저장된 문자열 읽어오기, 없다면 defValue 값이 읽어와진다.
val msg=pref.getString("msg", "")
/*
여기서 this는 MainActivity 객체를 가리킨다.
java 에서는 익명 클래스 안에서 바깥 클래스 객체의 참조값을 가리키려면
MainActivity.this 와 같이 클래스명을 명시했어야 한다.
kotlin 에서도 익명 클래스 안에서 바깥 클래스 객체의 참조값을 가리키려면
this@MainClass 와 같이 클래스명을 명시하면 된다.
단, 간략히 표현한 블럭 안에서는 this 만 써도 바깥 클래스 객체를 가리킬 수 있다.
*/
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
}
override fun onStart() {
super.onStart()
val pref=PreferenceManager.getDefaultSharedPreferences(this)
//액티비티가 활성화되는 시점에 설정에 저장된 값을 읽어오고 싶다면 여기서 작업하면 된다.
val signature=pref.getString("signature", "")
val reply=pref.getString("reply","")
val sync=pref.getBoolean("sync", false)
Toast.makeText(this, "signature:${signature}, reply:${reply}, sync:${sync}", Toast.LENGTH_LONG).show()
}
//저장 버튼을 누르면 호출되는 메소드
override fun onClick(v: View?) {
//EditText에 입력한 문자열 읽어오기
val msg=editText?.text.toString() //null 이 가능한 변수나 필드의 값을 참조할 때는 ? 가 필요하다.
//SharedPreferences 의 참조값 얻어오기
val pref:SharedPreferences=getSharedPreferences("info", Context.MODE_PRIVATE)
//에디터 객체의 참조값 얻어오기
val editor:SharedPreferences.Editor=pref.edit()
//에디터 객체를 이용해서 문자열을 key : value 형태로 영구 저장할 수 있다.
editor.putString("msg", msg)
editor.commit()
AlertDialog.Builder(this)
.setMessage("저장했습니다.")
.setNeutralButton("확인", null)
.create()
.show()
}
//옵션 메뉴를 구성하는 메소드
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//menu/menu_main.xml 문서를 전개해서 메뉴 구성하기
//in java => getMenuInflater().inflate()
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
//옵션 아이템을 선택했을 때 호출되는 메소드
override fun onOptionsItemSelected(item: MenuItem): Boolean {
//선택한 메뉴의 아이디 읽어오기
val id=item.itemId;
//만일 설정 메뉴를 선택했다면
if(id == R.id.setting){
//kotlin 에서 특정 클래스 type은 "클래스명::class.java" 라고 해야 한다.
val intent= Intent(this, SettingsActivity::class.java)
//SettingsActivity 를 시작하겠다는 의도를 가지고 있는 Intent 객체를 이용해서
//액티비티 시작시키기
startActivity(intent)
}
return true
}
}
- 코틀린에서 상속은 : 클래스명 () 으로 표시!
- 인터페이스 구현시에는 이렇게
- ? 는 null 을 허용하는 매개변수라는 뜻
- override 는 @ 없이 예약어만 사용한다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="206dp"
android:layout_height="69dp"
android:layout_marginTop="81dp"
android:ems="10"
android:hint="저장할 문자열 입력"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/saveBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:text="저장"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />
<Button
android:id="@+id/readBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
android:layout_marginEnd="2dp"
android:text="읽어오기"
app:layout_constraintEnd_toEndOf="@+id/saveBtn"
app:layout_constraintTop_toBottomOf="@+id/saveBtn" />
</androidx.constraintlayout.widget.ConstraintLayout>
- plaintext, button 요소 추가
- 참조값 얻어오기. 제너릭<> 을 빼면 타입 추론이 안된다. 빼면 오류가 난다.
- 반드시 어떤 타입인지 넣어주어야 한다!
- 타입 추론이 어려운경우, 어떤타입을 얻어올지 명시해야 한다.
- 위와 같이 제너릭을 넣거나, 변수에 타입을 명시하거나 둘중 하나는 해야한다.
- 버튼에 리스너 추가, View.OnClickListener 인터페이스 구현
- java의 작성법, kotlin 의 작성법 비교하며 익히기
- onClick 메소드 오버라이드 방법도 같다.(클래스 뒤에서 alt+enter)
** App에서 문자열을 영구 저장하는 방법
(영구 저장이란 앱을 종료하고 다시 시작해도 불러올 수 있는 문자열)
1. 파일 입출력을 이용해서 저장
2. android 내장 DataBase를 이용해서 저장
- SQLite DataBase (안드로이드에 기본으로 내장된 프로그램)
3. SharedPreference를 이용해서 저장 (느리지만 간단히 저장하고 불러올 수 있다)
- 내부적으로 xml 문서를 만들어서 문자열을 저장하고 불러온다.
- 저장된 문자열을 boolean, int, double, String type 으로 변환해서 불러올 수 있다.
- SharedPreference 를 먼저 사용해볼 예정!
- 느리기 때문에 입출력이 자주 일어나는 데이터는 사용에 적합하지 않다.
- 보통 앱의 설정 정보(자주 바뀌지 않는 정보) 등에 많이 사용한다.
- editText 필드를 만들어서 다른 메소드에서도 사용할수 있도록 하기.
- 하지만 kotlin 에서는 이렇게 작성하면 에러가 난다.
- ?=null 로 명시해주어야 한다.
- java에서는 field 를 선언만 하면 자동으로 null로 초기화된다.
- kotlin 에서는 null이 가능한 field 를 만들어서 명시적으로 넣어주어야 한다.
- 처음에는 null이었고, 얻어낸 참조값이 나중에 필드에 들어가는 형태
- 여기에도 ? 를 넣어주어야 오류가 발생하지 않는다.
- 앱을 종료하거나 컴퓨터를 껐다 켜도 이 문자열을 그대로 복구시키려면?
- getSharedPreference 메소드를 사용해준다.
- 이런 타입을 가지고 있다 (추론 가능해서 생략 가능하지만 확인을 위해 작성)
- put 메소드로 정수, 불리언, 실수 값 등이 가능하다.
editor.putString("msg", msg)
editor.commit()
- msg로 저장된 값을 editor에 넣어서 불러오기
- commit() 으로 영구 저장이 가능하다.
- 내부적으로는 안드로이드 기기의 파일시스템에 저장하는 것이다.
- 이렇게 알림이 뜨면서 저장된다.
- 우측 하단에 Device File Explorer 를 보면
이 디바이스의 파일 시스템이 나온다. (이것은 리눅스 파일시스템이라고 보면된다.)
- 윈도우라면 c: 의 목록이라고 보면 된다.
- 메모리로 구성된 파일 시스템이다.
- 원래는 이 폴더 안의 내용을 볼 수 없지만 이것은 가상기기이기 때문에 내용을 둘러볼 수 있다.
- 실제 기기라면 특별한 권한 없이는 이 내용을 볼 수 없다.
- 실제 기기의 내부시스템을 보려면 루팅(root 권한을 얻어내는 일)을 해야한다.
- data-data 폴더 : 여기에는 이 emulator에 인스톨된 어플리케이션이 사용하는 폴더들이 들어있다
- 그동안 만든 각각의 모듈의 패키지명과 똑같이 만들어진 폴더가 있다.
- 우리 앱이 사용하는 파일 시스템이 이곳인 것이다.
- step13 안에 들어가면 shared_pref 폴더가 있는데, xml 파일이 들어있다.
- 이 파일안에 담아서 commit한 것
- 저장하면 자동으로, 앱이 자체적으로 xml 문서를 만들고 key, value를 저장한다.
- 이 내용을 앱이 어디에 저장하는지 우리는 고려하지 않는다. 굳이 몰라도 된다.
- 에디터를 통해서 알아서 저장한다는것을 알면 된다.
- 에디터 객체를 사용해서 String을 담아주는 것!
- 값을 저장했다면 읽어오는 방법은?
- View.OnClickListener 를 구현한 Object이다.
- onClick 메소드를 override 해준다.
- 두개의 코드 비교하기
- onClick이라는 메소드 안쪽에 코딩한다. 인자로 view객체가 들어온다.
- 아래에서는 그 view객체를 it이라고 지칭할 수 있다.
- pref.getString("key 값", defValue) 로 읽어올 수 있다.
- defValue는 기본값이다. 값이 없을 경우를 대비해서 default 값도 준비해두는 것!
- 토스트 메시지에 필요한 Context를 this로 받아준다.(인터페이스 구현해서 가능)
- 읽어오기를 누르면 토스트 메시지에서 가져온다.
- 이 { } 는 하나의 함수이다. 코틀린의 함수 모양이라고 생각하면 된다.
- 메소드를 호출하면서 View타입을 인자로 전달받는 함수를 하나 전달한것과 같다.
- 추상메소드가 하나인경우엔 가능하다.
- 여기서 this는 익명클래스 안쪽에 있어서 다르게 작성해야 하는데,
- 간략히 표현하면 this만으로도 바깥 클래스를 가리킬 수 있다.
Toast.makeText(this@MainActivity, msg, Toast.LENGTH_SHORT).show()
- 원래는 이렇게 this@MainActivity 로 작성해야 한다.
- 이런 형태에서는 this를 적을 수 없다.
- 여기서의 this 는 listener 객체를 가리킨다.
- 이 안에서 this는 Context일수도있고 OnclickListener 일 수도 있다.
- 어떤 타입인지 모르겠으면 변수를 만들어서 타입을 넣어서 확인해보기
- 알아서 가능한 타입을 찾아주는 것이라고 보면 된다. this만 써도 Context 타입을 찾아주는 것이다!
참고) Kotlin 오토 import 설정
File-Settings
- auto import 검색
- Kotlin 위치의 2개를 체크해주면 자동으로 import 된다.
'국비교육(22-23)' 카테고리의 다른 글
76일차(1)/Android App(33) : SQLite 활용(1) (0) | 2023.01.26 |
---|---|
75일차(3)/Android App(32) : Shared Preferences(2) / menu (1) | 2023.01.26 |
75일차(1)/Android App(30) : Kotlin loop, try-catch (0) | 2023.01.25 |
74일차(5)/Android App(29) : Broadcast Receiver (0) | 2023.01.24 |
74일차(4)/Android App(28) : Bottom Navigation 예제 (0) | 2023.01.24 |