본문 바로가기

Study/Android

Android : 장면 전환 시 데이터 주고 받기

개요


접근

예를 들어서 어떤 화면에서 값을 입력받아 버튼을 누르면 다른 화면으로 이동해서 컨텐츠를 보여줘야 하는 경우가 있습니다.

Fragment를 사용하지 않고 아예 다른 화면으로 넘어가서 이전 화면에서 입력받은 정보를 불러와 처리해야 한다면 가장 기본적으로 DB에 입력받은 데이터를 저장하고 다른 화면에서 DB에 들어간 정보를 다시 불러와서 처리해주는 방식을 떠올릴 수 있을 것입니다.

그러나 간단한 숫자나 문자열 등을 DB에 저장해서 다시 이를 불러오는 작업은 그렇게 효율적이라는 생각은 들지 않는 것 같습니다.

Spring에서 Model이라는 객체를 사용하여 addAttribute로 전달해줄 값을 설정해주고 이를 불러올 곳에서 getAttribute로 값을 불러와 사용했습니다.

마찬가지로 안드로이드에서는 getSharedPreferences를 사용할 수 있습니다. 그러나 getSharedPreferences는 기본 자료형의 형태만 전달할 수 있다는 단점이 있습니다.

다시 말해서 object 형태는 전달해 줄 수 없다는 뜻인데, object를 전달해주기 위해서 Parcelable를 사용할 수 있습니다.

또는 값을 서로 전달하여 받는 방식 이외에 싱글턴이라는 공동 공유공간을 만들어서 값을 끌어올 수도 있습니다.

그러면 지금부터 각각을 어떻게 사용할 수 있는지 살펴보겠습니다.

getSharedPreferences


메인 레이아웃 구성

장면 전환을 위해 레이아웃을 두개 만들 것입니다. 우선 메인 레이아웃을 다음과 같이 구성하였습니다

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <EditText
            android:id="@+id/editText"
            android:layout_width="496dp"
            android:layout_height="54dp"
            android:inputType="textPersonName"
            android:ems="10"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintVertical_bias="0.158"/>
    <Button
            android:id="@+id/write"
            android:text="Preference 쓰기"
            android:layout_width="495dp"
            android:layout_height="54dp" 
            app:layout_constraintTop_toBottomOf="@+id/editText"
            app:layout_constraintStart_toStartOf="parent" 
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintHorizontal_bias="0.504"
            app:layout_constraintVertical_bias="0.098"/>
    <Button
            android:id="@+id/move"
            android:text="다음 화면으로 이동"
            android:layout_width="495dp"
            android:layout_height="54dp"
            app:layout_constraintTop_toBottomOf="@+id/editText"
            app:layout_constraintStart_toStartOf="parent" 
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintHorizontal_bias="0.504"
            app:layout_constraintVertical_bias="0.242"/>
</androidx.constraintlayout.widget.ConstraintLayout>

서브 레이아웃 구성

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".SecondActivity">

    <TextView
            android:id="@+id/textView"
            android:text="TextView"
            android:layout_width="395dp"
            android:layout_height="127dp"
            android:textSize="30dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintVertical_bias="0.283"/>
    <Button
            android:id="@+id/read"
            android:text="Preference 읽기"
            android:layout_width="431dp"
            android:layout_height="80dp"
            app:layout_constraintTop_toBottomOf="@+id/textView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.502"
            app:layout_constraintVertical_bias="0.255"/>
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity

레이아웃을 구성하였으니 이제 버튼을 클릭했을 때 다음 화면으로 넘겨줄 정보를 입력받고 다음화면으로 넘겨서 그 정보를 가져와 보겠습니다.

메인 화면에서는 두개의 버튼을 준비했습니다. 넘겨줄 정보를 저장해주고 다음 페이지로 넘기는 작업을 해보겠습니다.

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val write = findViewById<Button>(R.id.write)
        val move = findViewById<Button>(R.id.move)

        val edit = findViewById<EditText>(R.id.editText)

우선 컴포넌트들을 아이디 값으로 불러옵니다.

        write.setOnClickListener {
            val pref = getSharedPreferences("pref", MODE_PRIVATE)
            val editor = pref.edit()        // 초기화
            editor.putString("myData", edit.text.toString())
            editor.apply()
            edit.setText("")
        }

변수 write로 지정된 버튼을 클릭했을 때 이벤트 리스너입니다.

pref 변수에는 getSharedPreferences를 넣어주었습니다. 이 넘겨줄 정보의 이름이 "pref"이며 MODE_PRIVATE로 지정하여 새로운 값을 넣어줄 때마다 덮어쓰기 하게 해주었습니다.

editor 변수에는 pref를 초기화 하기 위한 것이 들어있습니다.

putString을 통해 "myData"라는 이름으로 사용자 입력 컴포넌트에 있는 값을 문자열로 넣어줍니다.

apply()를 통해 적용해주고 작업이 완료되면 빈칸으로 세팅합니다.

        move.setOnClickListener {
            val i = Intent(this, SecondActivity::class.java)
            startActivity(i)
        }

    }
}

다음 페이지로 이동하는 버튼을 눌렀을 때 SecondActivity로 넘겨줍니다.

Activity는 앱 안의 단일 화면을 나타냅니다. Activity의 새 인스턴스를 시작하려면 Intent를 startActivity()로 전달하면 됩니다. Intent는 시작할 액티비티를 설명하고 모든 필수 데이터를 담습니다.
- 안드로이드 개발자 문서

다시 말해서 Intent를 통해 이 페이지에서 다른 페이지로 넘겨줄 것임을 명시해주고 startActivity로 전달하여 다음 페이지로 이동하게 해주었습니다.

SecondActivity에서 불러오기

getString을 사용해서 불러옵니다.

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val read = findViewById<Button>(R.id.read)
        val textView = findViewById<TextView>(R.id.textView)

        read.setOnClickListener {
            val pref = getSharedPreferences("pref", MODE_PRIVATE)
            textView.text = pref.getString("myData", "")
        }
    }
}

Parcelable


메인 화면 레이아웃 구성

Parcelable을 살펴보기 위해 메인 레이아웃과 서브 레이아웃을 만들어주겠습니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <EditText
            android:id="@+id/editName"
            android:layout_width="392dp"
            android:layout_height="55dp"
            android:inputType="textPersonName"
            android:ems="10"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintVertical_bias="0.193"
    />
    <EditText
            android:id="@+id/editCount"
            android:layout_width="392dp"
            android:layout_height="55dp"
            android:inputType="textPersonName"
            android:ems="10"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintVertical_bias="0.279"
    />
    <EditText
            android:id="@+id/editLevel"
            android:layout_width="392dp"
            android:layout_height="55dp"
            android:inputType="textPersonName"
            android:ems="10"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintVertical_bias="0.369"
    />
    <Button
            android:id="@+id/move"
            android:text="Button"
            android:layout_width="393dp"
            android:layout_height="53dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintVertical_bias="0.525"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.497"
    />
</androidx.constraintlayout.widget.ConstraintLayout>

서브 레이아웃 구성

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".SecondActivity">
    <TextView
            android:id="@+id/textView"
            android:text="TextView"
            android:textSize="30dp"
            android:layout_width="413dp"
            android:layout_height="130dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_bias="0.502"
            app:layout_constraintVertical_bias="0.187"/>
    <Button
            android:id="@+id/read"
            android:text="읽기"
            android:layout_width="271dp"
            android:layout_height="57dp"
            app:layout_constraintTop_toBottomOf="@+id/textView"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.501"
            app:layout_constraintVertical_bias="0.239"/>
</androidx.constraintlayout.widget.ConstraintLayout>

DTO 구성

object 전달 연습을 위해 DTO를 하나 만들어주겠습니다.

package dto

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

class Student(var name: String?, var count:Int, var level: String?) : Parcelable {

    constructor(parcel: Parcel):this(parcel.readString(), parcel.readInt(), parcel.readString()) {}


    override fun writeToParcel(parcel: Parcel?, p1: Int) {
        parcel?.writeString(name)
        parcel?.writeInt(count)
        parcel?.writeString(level)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Student> {
        override fun createFromParcel(parcel: Parcel): Student {
            return Student(parcel)
        }

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


}

우리는 장면 전환 시 객체를 전달해줄 것이기 때문에 전달해줄 형태의 객체는 Parcelable을 상속받아야 합니다.

Parcelable을 상속받았기 때문에 writeToParceldescribeContents의 오버라이드가 필요합니다.

또한 companion object의 형태로 CREATOR가 구현되어야 합니다.

MainActivity

역시 버튼 클릭시 이벤트를 발생시켜보겠습니다.

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import dto.Student

// preference는 기본자료형만 보낼 수 있어서...
// intent를 사용하면 객체를 전달해줄 수 있다!
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val editName = findViewById<EditText>(R.id.editName)
        val editCount = findViewById<EditText>(R.id.editCount)
        val editLevel = findViewById<EditText>(R.id.editLevel)

        val move = findViewById<Button>(R.id.move)

        move.setOnClickListener {
            var student = Student(editName.text.toString(), editCount.text.toString().toInt(), editLevel.text.toString())
            val nextIntent = Intent(this, SecondActivity::class.java)
            nextIntent.putExtra("student", student)
            startActivity(nextIntent)
        }

    }
}

버튼을 클릭했을 때 객체를 생성하여 생성에 필요한 것들을 넣어주었습니다.

그리고 다음 페이지로 넘어가기 위해 Intent를 사용하였습니다.

이 때 putExtra를 사용하여 "student"라는 이름으로 student 변수에 저장된 객체를 넘겨줍니다.

SubActivity

메인 페이지에서 다음 화면으로 넘어왔을 때 처리해 줄 것들입니다.

import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import dto.Student

class SecondActivity : AppCompatActivity() {
    @SuppressLint("SetTextI18n")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)

        val textView = findViewById<TextView>(R.id.textView)

        val read = findViewById<Button>(R.id.read)
        read.setOnClickListener {
            val student = intent.getParcelableExtra<Student>("student")
            if (student != null) {
                textView.text = "${student.name} ${student.count} ${student.level}"
            }
        }
    }
}

read 버튼 클릭 시 getParcelableExtra로 메인 화면에서 전달해 준 "student"를 받아옵니다.

null이 아닌 경우 텍스트뷰에 받아온 정보를 뿌려줍니다.

Singleton


싱글톤은 전역변수를 선언하지 않더라도 싱글톤 객체 하나만을 생성하여 어디서든 참조할 수 있게 해줍니다.

메인 레이아웃

화면 이동간 싱글톤을 통해 메인 레이아웃에서 입력한 텍스트를 서브 레이아웃에서 받아올 것입니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <EditText
            android:id="@+id/editText"
            android:layout_width="428dp"
            android:layout_height="72dp"
            android:inputType="textPersonName"
            android:text="Name"
            android:ems="10"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintVertical_bias="0.382"/>
    <Button
            android:id="@+id/move"
            android:text="Button"
            android:layout_width="264dp"
            android:layout_height="52dp" 
            app:layout_constraintTop_toBottomOf="@+id/editText"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintVertical_bias="0.25"/>
</androidx.constraintlayout.widget.ConstraintLayout>

서브 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".SubActivity">

    <TextView
            android:id="@+id/textView"
            android:text="TextView"
            android:textSize="30sp"
            android:layout_width="334dp"
            android:layout_height="84dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" 
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

싱글톤 생성

class Singleton {

    companion object {

        var chicken:String? = null
    }
}

싱글톤은 반드시 companion object가 선언되어야 합니다.

이 싱글톤 객체는 어디서든 참조할 수 있게 됩니다. 초기값을 null로 지정하였습니다.

MainActivity

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val editText = findViewById<EditText>(R.id.editText)
        val move = findViewById<Button>(R.id.move)

        move.setOnClickListener {
            // 싱글턴 호출 및 적용
            Singleton.chicken = editText.text.toString()

            // 이동
            val intent = Intent(this, SubActivity::class.java)
            startActivity(intent)
        }

    }
}

싱글톤을 호출할 때는 Singleton.으로 할 수 있으며 내부에 선언된 변수를 붙여주면 됩니다.

SubActivity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class SubActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sub)

        val textView = findViewById<TextView>(R.id.textView)

        textView.text = Singleton.chicken.toString()
    }
}

싱글톤에 있는 정보를 가져올 때도 Singleton.으로 불러올 수 있습니다.

'Study > Android' 카테고리의 다른 글

Android : Camera  (0) 2022.02.16
Android : SQLite  (0) 2022.02.16
Android : JSON 파싱하기  (0) 2022.02.16
Android : 파일 입출력  (0) 2022.02.16
Android : DatePicker와 TimePicker  (0) 2022.02.16