💻STUDY/ANDROID STUDY

[Android-Kotlin] 6주차

coldNoodlePigeon 2022. 5. 16.

 

 

화면 구성하기: 액티비티 값 주고받기 뷰바인딩

package com.example.activity

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

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) { //Entry Point (시작점)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

안드로이드의 시작점 (entry point)는 AndroidManifest.xml에 정의되어있다. 드래그하여 표시한 부분이 안드로이드의 entry point라고 할 수 있음. 

 

com.example.activity 우클릭>New>Activity>Empty Activity 로 SubActivity 생성 가능.

액티비티명 끝에 Activity라고 붙이면 Layout 명이 activty_어쩌고로 생성된다. 

해당 액티비티를 시작점으로 하고 싶을 시 Launcher Activity 항목을 체크해주면 된다. 

 

SubActivity가 생성된 모습

SubActivity를 시작점으로 하고 싶을시 드래그 해놓은 해당 부분(<intent-filter>)를 해당 SubActivity <activity></> 안에 넣어주면 된다. 

 

package com.example.activity

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.activity.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) { //Entry Point (시작점)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding.btnStart.setOnClickListener {
            val intent = Intent(this,SubActivity::class.java)
            startActivity(intent)
        }
    }
}

버튼이 클릭되면 서브액티비티를 실행하도록 하는 MainActivity 코드. 

 

 

버튼이 클릭되면 putExtra로 전달된 값이 SubActivity로 전달되어 서브액티비티에서 그 화면을 보여주도록 하였다. 아래는 MainActivity 코드이다. 

package com.example.activity

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.activity.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) { //Entry Point (시작점)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding.btnStart.setOnClickListener {
            val intent = Intent(this,SubActivity::class.java)

            //Intent의 값이 담기는 공간: Bundle
            intent.putExtra("from1","Hello Bundle")
            intent.putExtra("from2",2022)
            //value에 들어가는 타입에 따라 자동으로 타입이 결정됨.

            startActivity(intent)
        }
    }
}
package com.example.activity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.activity.databinding.ActivitySubBinding

class SubActivity : AppCompatActivity() {

    val binding by lazy { ActivitySubBinding.inflate(layoutInflater) }

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

        with(binding){
            to1.text=intent.getStringExtra("from1") //SubActivity가 intent값을 가져옴.
            to2.text="${intent.getIntExtra("from2",0)}"
            //만약 받아오는 key값이 없을 경우 default 값을 정해줌.
            //text에는 정수형 int를 바로 가져올 수 없으므로 문자열 템플릿 사용함.
        }


    }
}

SubActivity로 intent의 값이 전달된다. 이때, value값에 따라 getExtra를 다르게 호출해야함에 주의한다. 

 

 

다음으로는 SubActivity에서 MainActivity로 전달하는 경우이다. 메소드로는 startActivityForResult(intent,requestCode)를 사용한다. (현재는 잘 사용하지 않는 기능인듯.)

package com.example.activity

import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import com.example.activity.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) { //Entry Point (시작점)
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        binding.btnStart.setOnClickListener {
            val intent = Intent(this,SubActivity::class.java)

            //Intent의 값이 담기는 공간: Bundle
            intent.putExtra("from1","Hello Bundle")
            intent.putExtra("from2",2022)
            //value에 들어가는 타입에 따라 자동으로 타입이 결정됨.

            startActivityForResult(intent,99)
        }
    }

    //아래 resultCode로 SubActivity의 RESULT.OK가 전달됨.
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if(resultCode==Activity.RESULT_OK){
            when(requestCode){
                99->{
                    //subActivity에서 returnValue라는 id(name)로 전달하므로 "returnValue"로 받음.
                    //val message=data?.getStringExtra("returnValue")
                    //로 해도 되고,

                    //또는 let scope함수를 사용하여 return value값이 없을 경우를 처리 가능하다.
                    data?.getStringExtra("returnValue").let{ message->
                        Toast.makeText(this,message,Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    }
}

ctrl+O에서 onActivityResult 함수메소드를 불러온다. 

package com.example.activity

import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.activity.databinding.ActivitySubBinding

class SubActivity : AppCompatActivity() {

    val binding by lazy { ActivitySubBinding.inflate(layoutInflater) }

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

        with(binding){
            to1.text=intent.getStringExtra("from1") //SubActivity가 intent값을 가져옴.
            to2.text="${intent.getIntExtra("from2",0)}"
            //만약 받아오는 key값이 없을 경우 default 값을 정해줌.
            //text에는 정수형 int를 바로 가져올 수 없으므로 문자열 템플릿 사용함.


            btnClose.setOnClickListener {
                val returnIntent= Intent()
                //닫은 후에 자신을 호출한 쪽으로 전달하는 intent이기 때문에 MainActivity 쓸 필요 없음.

                val message=editMessage.text.toString()
                returnIntent.putExtra("returnValue",message)
                //정상적으로 종료되었음을 result_ok로 mainactivity측에 알려줌.
                setResult(Activity.RESULT_OK,returnIntent)
                finish()
            }
        }


    }
}

edittext에 값을 입력하고 버튼을 클릭하면 화면이 닫기면서 main activity로 edittext의 입력값이 전달되도록하였다. 성공적으로 전달되면 메인 액티비로 RESULT.OK를 함께 전송한다. 

 

 

 

화면 구성하기: 스피너 

package com.example.spinner

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import com.example.spinner.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

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


        var data=listOf("-선택하세요-","1월","2월","3월")

        //스피너 콘테이너에 데이터를 넣기 위해서는 중간에 매개 역할이 필요함 -> adaptor
        //문자열의 컬렉션이므로 <String>으로 함.
        //거의 안드로이드에서 제공하는 클래스들은 context에 this가 많이 들어감 (ex. Intent..)
        //두번째 매개변수에는 스피너가 보여주는 목록의 레이아웃 파일을 지정해줌.
        //안드로이드에 만들어져있는 아래와 같은 레이아웃을 사용해줄 수 있고, 또는 직접 만들어줄수도 있음.
        //3번째 매개변수에는 넣어줄 콘텐트들을 넣는다.
        var adaptor= ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data)

        binding.spinner.adapter=adaptor
    }
}

 

 

package com.example.spinner

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import com.example.spinner.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

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


        var data=listOf("-선택하세요-","1월","2월","3월")

        //스피너 콘테이너에 데이터를 넣기 위해서는 중간에 매개 역할이 필요함 -> adaptor
        //문자열의 컬렉션이므로 <String>으로 함.
        //거의 안드로이드에서 제공하는 클래스들은 context에 this가 많이 들어감 (ex. Intent..)
        //두번째 매개변수에는 스피너가 보여주는 목록의 레이아웃 파일을 지정해줌.
        //안드로이드에 만들어져있는 아래와 같은 레이아웃을 사용해줄 수 있고, 또는 직접 만들어줄수도 있음.
        //3번째 매개변수에는 넣어줄 콘텐트들을 넣는다.
        var adaptor= ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data)

        binding.spinner.adapter=adaptor

        binding.spinner.onItemSelectedListener=object: AdapterView.OnItemSelectedListener{
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                //선택하면 동작하는 메소드
                val selected= data.get(p2) //data에서 값을 가져와서 selected에 넣어줌.
                binding.result.text=selected
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {
                //아무것도 선택되지 않았을때 동작되는 메소드
            }

        }

    }
}

 

onItemSelectedListener기능을 사용하여 스피너의 항목값을 선택하면 동작하는 메소드를 작성해준다. 

 

다음과 같이 실행되는 것을 확인 가능하다. 

 

package com.example.spinner

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import com.example.spinner.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}

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


        var data=listOf("-선택하세요-","1월","2월","3월")

        //스피너 콘테이너에 데이터를 넣기 위해서는 중간에 매개 역할이 필요함 -> adaptor
        //문자열의 컬렉션이므로 <String>으로 함.
        //거의 안드로이드에서 제공하는 클래스들은 context에 this가 많이 들어감 (ex. Intent..)
        //두번째 매개변수에는 스피너가 보여주는 목록의 레이아웃 파일을 지정해줌.
        //안드로이드에 만들어져있는 아래와 같은 레이아웃을 사용해줄 수 있고, 또는 직접 만들어줄수도 있음.
        //3번째 매개변수에는 넣어줄 콘텐트들을 넣는다.
        var adaptor= ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data)

        with(binding){
            spinner.adapter=adaptor

            spinner.onItemSelectedListener=object: AdapterView.OnItemSelectedListener{
                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    //선택하면 동작하는 메소드
                    val selected= data.get(p2) //data에서 값을 가져와서 selected에 넣어줌.
                    result.text=selected
                }

                override fun onNothingSelected(p0: AdapterView<*>?) {
                    //아무것도 선택되지 않았을때 동작되는 메소드
                }

            }
        }


    }
}

with 스코프함수로 깔끔하게 정리가능하다. 

 

 

화면 구성하기: 리사이클러뷰 

다음과 같이 위젯들을 배치한다.

 

다음 res/layout폴더에 new resource file을 추가하여 레이아웃을 생성하고, 

코틀린 클래스 파일을 하나 추가 생성하여 코드를 작성한다. 

 

package com.example.recycleview

data class Memo (var num:Int, var title:String,var timestamp:Long)

val memolst= mutableListOf<Memo>()

for(idx in 1..100){
    val memo= Memo(idx,"헬로", 12345678)
    memolst.add(memo)
}

위와 같은 코드들을 작성하여 라사이클러뷰의 아이템 레이아웃들을 하나씩 넣어주는 기능을 하도록 할 수 있다. 

 

package com.example.recycleview

data class Memo (var no:Int, var title:String,var timestamp:Long)

Memo 클래스의 내용이다.

 

package com.example.recycleview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.recycleview.databinding.ActivityMainBinding
import com.example.recycleview.databinding.ItemRecyclerBinding
import java.text.SimpleDateFormat

class MainActivity : AppCompatActivity() {

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

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

        //1. 데이터를 불러온다
        val data=loadData()

        //2. 어댑터를 생성
        val customAdapter=CustomAdapter(data)

        //3. 화면의 RecyclerView와 연결
        binding.recyclerView.adapter=customAdapter

        //4. 레이아웃 매니저 설정
        binding.recyclerView.layoutManager=LinearLayoutManager(this)

    }

    fun loadData(): MutableList<Memo>{
        val memoList= mutableListOf<Memo>()
        for(no in 1..100){
            val title="이것이 안드로이드다. $no"
            val date=System.currentTimeMillis()
            val memo=Memo(no,title,date)
            memoList.add(memo)
        }
        return memoList
    }
}

//아래의 adapter가 holder를 가지고 아이템 레이아웃의 값을 세팅한다는 의미
class CustomAdapter(val listData:MutableList<Memo>): RecyclerView.Adapter<CustomAdapter.Holder>(){

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding=ItemRecyclerBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        //1. 사용할 데이터를 꺼내고
        val memo=listData.get(position)
        //2. 홀더에 데이터를 전달
        holder.setMemo(memo)
    }

    override fun getItemCount(): Int {
        return listData.size
    }

    //RecyclerView의 ViewHolder을 상속받음. 상속받을때 생성자로 사용하는 View를 넘겨줘야.
    class Holder(val binding:ItemRecyclerBinding):RecyclerView.ViewHolder(binding.root){
        //클릭처리는 init에서만 한다.
        lateinit var currentMemo:Memo
        init{
            binding.root.setOnClickListener {
                val title=binding.textTitle.text
                Toast.makeText(binding.root.context,"클릭된 아이템 : $title",Toast.LENGTH_SHORT).show()
            }
        }

        //3. 받은 데이터를 화면에 출력한다.
        fun setMemo(memo:Memo){

            //클릭을 했을때 현재 메모 정보를 표시.
            currentMemo=memo

            with(binding){
                textNo.text="${memo.no}"
                textTitle.text=memo.title

                val sdf=SimpleDateFormat("yyyy-MM-dd")
                val formattedDate=sdf.format(memo.timestamp)
                textDate.text=formattedDate
            }
        }
    }
}

 

 

화면 구성하기: 프래그먼트 

 

부드럽게 화면전환하고싶을때 프래그먼트 전체를 사용함으로써 구현 가능 

화면전환 등등 

와 같이 fragment를 만들어서 layout속성으로 해당 xml을 넣어주면 미리보기로 보는 것도 가능하다. 

 

이보다 framelayout으로 구성하는 것이 더 훨씬 화면넘김이 부드럽다. 

package com.example.fragment

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.fragment.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    val binding by lazy{ActivityMainBinding.inflate(layoutInflater)}
    //val listFragment by lazy{ListFragment()}

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

        setFragment()
    }

    fun setFragment(){
        // 1. 사용할 프래그먼트 생성
        val listFragment = ListFragment()

        //2. 트랜잭션 생성
        val transaction=supportFragmentManager.beginTransaction()

        //3. 트랜잭션을 통해서 프래그먼트 삽입
        transaction.add(R.id.listFragment,listFragment)
        transaction.commit()
    }
}

framelayout으로 화면을 구성하고 다음과 같은 MainActivity코드를 작성했다. 

 

package com.example.fragment

import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.fragment.databinding.ActivityMainBinding
import com.example.fragment.databinding.FragmentListBinding


class ListFragment : Fragment() {

    lateinit var mainActivity: MainActivity
    lateinit var binding: FragmentListBinding

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if(context is MainActivity) mainActivity=context
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        binding= FragmentListBinding.inflate(inflater,container,false)

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.btnNext.setOnClickListener {
            mainActivity.goDetail()
        }
    }
}

listFragment의 class 코드

 

 

package com.example.fragment

import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.fragment.databinding.FragmentDetailBinding
import com.example.fragment.databinding.FragmentListBinding

class DetailFragment : Fragment() {
    lateinit var mainActivity: MainActivity
    lateinit var binding: FragmentDetailBinding

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if(context is MainActivity) mainActivity=context
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        binding= FragmentDetailBinding.inflate(inflater,container,false)

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.btnBack.setOnClickListener {
            mainActivity.goBack()
        }
    }


}

detailFragment의 클래스 코드 

'💻STUDY > ANDROID STUDY' 카테고리의 다른 글

8주차 스터디 정리  (0) 2022.07.18
[Android-Kotlin] 7주차  (0) 2022.05.25
[Android-Kotlin] 5주차  (0) 2022.05.09
[Android-Kotlin] 4주차  (0) 2022.05.02
[Android-Kotlin] 3주차  (0) 2022.04.02

댓글