🧨 주요 내용 : Intent 사용법
메인 액티비티로 가는 버튼과 앱 종료 버튼 생성
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:padding="16dp"
tools:context=".IntrolActivity">
<Button
android:id="@+id/btn"
android:text="go to main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<Button
android:id="@+id/btn_exit"
android:text="Exit"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
- nullable 변수 만들기 ? 키워드 사용 (사용할 땐 ?.)
- var의 늦은 초기화
- object : 익명 객체 쓰는 키워드 (코딩쓰는 영역인 중괄호만 쓴다) 람다를 더 축약
- 샘(SAM : Single Abstract Method) 변환
- Intent 객체 생성
바깥 거 부를 때 라벨표시로 , ::class.java(:: 클래스 부르는 키워드) -> 아직 Intent가 java로 인식 돼서 .java가 붙어 있음
package com.bsj0420.ex02kotlinrecyclerview
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.View.OnClickListener
import android.widget.Button
class IntrolActivity : AppCompatActivity() {
//코틀린은 멤버변수(프로퍼티)를 초기화 하지 않으면 Error
var btn:Button? = null // nullable 변수
//늦은 초기화
lateinit var btnExit:Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_introl)
//버튼 객체 참조
btn = findViewById(R.id.btn)
// nullable변수는 null일수도 있기 떄문에 안전하게 멤버에 접근하는 연산자 사용해야함
btn?.setOnClickListener(object : OnClickListener{ //object : 익명 객체 쓰는 키워드
override fun onClick(p0: View?) {
val intent:Intent = Intent(this@IntrolActivity,MainActivity::class.java)
//바깥 거 부를 때 라벨표시로 , ::class.java(:: 클래스 부르는 키워드) -> 아직 Intent가 java로 인식 돼서 .java가 붙어 있음
startActivity(intent)
}
})
//btnExit 참조
btnExit = findViewById(R.id.btn_exit)
//1. 리스너 설정을 익명클래스 말고 간결하게 람다식으로
btnExit.setOnClickListener({v-> finish()}) //코틀린에선 중괄호 안에 v->
//2. 파라미터가 1개라면 생략가능[ 자동 it 키워드로 파라미터명 지정됨 ]
btnExit.setOnClickListener({
finish()
})
//3. 람다를 더 축약 -> 샘(SAM : Single Abstract Method) 변환 -> 코딩쓰는 영역인 중괄호만 쓴다
btnExit.setOnClickListener{
finish()
}
}
}
🧨 주요 내용 : 리사이클러 뷰 사용
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="8dp"
android:elevation="8dp"
android:layout_margin="12dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv"
android:src="@drawable/moana01"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tv_name"
android:layout_margin="16dp"
android:layout_alignParentRight="true"
android:textStyle="bold"
android:textSize="40sp"
android:textColor="@color/white"
android:text="name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_msg"
android:layout_margin="16dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/white"
android:text="message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
- 주생성자 사용
- 주생성자에 var 키워드 있으면 매개변수면서 멤버변수가 됨
- 클래스 영역에 아무것도 쓸게 없으면 {} 없어도 됨
💡 data 클래스
- 데이터 저장 목적인 애들은 앞에 data 키워드를 붙인다
- 일반 클래스와 다르게 자동으로 equls() 할 때 객체 주소를 비교하지 않고 멤버값을 비교해주도록 설계된 클래스
- 단, 주 생성자의 멤버만 비교한다
package com.bsj0420.ex02kotlinrecyclerview
data class Item constructor(var name:String, var msg:String, var imgId:Int)
- 상속 키워드 : & 상속 받을 때 () 생성자 빼먹지 않도록 조심!
- 생성자에 Context와 MutableList 받아옴
생성자 키워드 construct 생략 가능
- viewholder 이너 클래스 만들 때 inner 키워드!
ViewHolder를 상속 받을 때 constructor()로 View를 받는다
(레이아웃 안에 있는 뷰들을 들고 오는 거니까 View를 생성자로 받음)
itemView는 itemlayout의 최상위 카드뷰를 부른 것
- onCreateViewHolder : 레이아웃 그리는 곳 (LayoutInflater)
- onBindViewHolder
코틀린은 리스의 get() set() 싫어함 따라서 get 대신에 배열처럼 [] 씀 (인덱스 번호 를 권장)
- getItemCount() : 함수 리턴 값 - 할당 연산자로 단순화
package com.bsj0420.ex02kotlinrecyclerview
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
// 상속 키워드 : & 상속 받을 때 () 생성자 빼먹지 않도록 조심!
// 생성자에 Context와 MutableList 받아옴
class MyAdapter(var context:Context, var items:MutableList<Item>) : Adapter<MyAdapter.VH>() {
//이너클래스는 inner 키워드!
//ViewHolder 만들 때 constructor()로 View를 받는다
// itemView는 itemlayout의 최상위 카드뷰를 부른 것
inner class VH(itemView:View) : ViewHolder(itemView) {
val tvName:TextView by lazy { itemView.findViewById(R.id.tv_name) }
val tvMsg:TextView by lazy { itemView.findViewById(R.id.tv_msg) }
val iv:ImageView by lazy { itemView.findViewById(R.id.iv) }
}
//아이템뷰 레이아웃 불러오기
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
var itemView:View = LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false)
return VH(itemView)
}
override fun onBindViewHolder(holder: VH, position: Int) {
//var item:Item = items.get(position)
//코틀린은 리스의 get() set() 대신에 배열처럼 [] - 인덱스 번호 를 권장
var item:Item = items[position]
holder.tvName.setText(item.name)
//코틀린은 get/ set권장하지 않음
holder.tvMsg.text = item.msg
Glide.with(context).load(item.imgId).into(holder.iv)
}
// 함수 리턴 값 - 할당 연산자로 단순화
override fun getItemCount(): Int = items.size
}
옵션 메뉴에 들어갈 메뉴 아이템 지정
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/aaa" android:title="aaa" />
<item android:id="@+id/bbb" android:title="bbb"/>
<item android:id="@+id/ccc" android:title="ccc"/>
</menu>
- val 의 늦은 초기화 : by lazy
- MutableList 객체 생성 방법
new 로 객체 생성하지 않고 mutableListOf() 를 쓴다
- 리사이클러뷰에 아답터 설정
아답터 참조 객체 안만들어도 된다 - 바로 넣어줌
코드로 LinearLayoutManager 설장하는 방법- onResume() 에서 데이터들을 갱신하는 작업
recycler.adapter?.notifyDataSetChanged()
리사이클러 아답터한테 notify- onCreateOptionsMenu : 옵션 메뉴 만드는 콜백 메소드
- onOptionsItemSelected : 옵션 메뉴 아이템 선택하면 자동으로 발동하는 콜백 메소드
package com.bsj0420.ex02kotlinrecyclerview
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
//뷰 참조변수는 참조값이 변경되지 않기에 var보다는 val로 만들어보기
//늦은 초기화
val recycler:RecyclerView by lazy {
findViewById(R.id.recycler)
}
//대량의 데이터들
var items:MutableList<Item> = mutableListOf() //mutableListOf() 객체 만들어 주는 애
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//대량의 데이터 추가 (테스트 목적)
items.add(Item("sam","hihi",R.drawable.moana01))
items.add(Item("Lisa","ohohoho yeah",R.drawable.moana02))
items.add(Item("jeni","moana",R.drawable.moana03))
items.add(Item("jisoo","blackpink",R.drawable.moana04))
items.add(Item("rose","in your area",R.drawable.moana05))
//리사이클러뷰에 아답터 설정
//아답터 참조 객체 안만들어도 됨 - 바로 넣어줌
recycler.adapter = MyAdapter(this,items)
//notify 할 땐 - recycler.adapter한테 노티파이
//리사이클러뷰에 레이아웃매니저 설정 - 레이아웃에 하면 되는데 연습하기 위해 여기서 설정함
recycler.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
}
override fun onResume() {
super.onResume()
//보통 이곳에서 데이터들을 갱신하는 작업들이 이루어짐
// adapter은 nullable 변수임 따라서 ? 연산자 붙이기
recycler.adapter?.notifyDataSetChanged()
//recycler.adapter!!.notifyDataSetChanged() //무조건 널이 아니다 연산자
}
//옵션 메뉴 만드는 콜백 메소드
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
//자바처럼 menuInflater를 get하는 작업필요없이 액티비티에 이미 menuInflater 객체 존재
menuInflater.inflate(R.menu.option, menu)
return super.onCreateOptionsMenu(menu)
}
//옵션 메뉴 아이템 선택하면 자동으로 발동하는 콜백 메소드
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId) {
R.id.aaa -> Toast.makeText(this, "aaa 클릭", Toast.LENGTH_SHORT).show()
R.id.bbb -> {
Toast.makeText(this, "bbb 클릭", Toast.LENGTH_SHORT).show()
}
R.id.ccc -> Toast.makeText(this, "ccc 클릭", Toast.LENGTH_SHORT).show()
}
return super.onOptionsItemSelected(item)
}
}
adjustViewBounds : 이미지 뷰 쓸때 warp 쓰면 필수
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:padding="16dp"
tools:context=".ItemDetailActivity">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"/>
<TextView
android:id="@+id/tv"
android:padding="8dp"
android:textColor="@color/black"
android:textStyle="bold"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
- 넘어온 인텐트 객체가 가져온 Extra 데이터 받기
- 제목줄에 이름 표시
- 텍스트 뷰에 대입
- 전환효과를 줄 뷰에게 adapter에서 지정한 이름 붙여주기
package com.bsj0420.ex02kotlinrecyclerview
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
class ItemDetailActivity : AppCompatActivity() {
val iv:ImageView by lazy { findViewById(R.id.iv) }
//참조변수 자료형을 자동추론시키려면 .. find 할 때 제네릭으로 자료형 알려줘야함
val tv by lazy { findViewById<TextView>(R.id.tv) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_item_detail)
//나한테 넘어온 인텐트 객체가 가져온 Extra 데이터 받기
var name:String? = intent.getStringExtra("name")
var msg:String = intent.getStringExtra("msg") as String //null을 String으로 바꿔도 됨
var imgId:Int = intent.getIntExtra("imgId",R.drawable.penguins)
//제목줄에 이름 표시
supportActionBar!!.title = name
//메세지는 텍스트 뷰에 표시
tv.text = msg
//이미지 보여주기
Glide.with(this).load(imgId).into(iv)
////////////////////////////////
//전환효과를 줄 뷰에게 별칭 지정하기
iv.transitionName = "iii"
}
}
🧨 주요 내용 : 화면 전환 효과
ActivityOptionsCompat
options 을 줄 떄 번들(꾸러미)로 만들어서 줘야함 옵션이 한두개가 아니니까
ActivityOptionsCompat.makeSceneTransitionAnimation(context as MainActivity, Pair(holder.iv, "iii")
1. MainActivity의 능력을 넘겨받은 context를 MainActivity로 형변환 해서 넣어줌
2. Pair() 객체 - 리사이클러뷰의 선택된 item에게 별명 붙이고 전환될 뷰에도 같은 별명을 붙여 한 쌍으로 만든다
package com.bsj0420.ex02kotlinrecyclerview
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
// 상속 키워드 : & 상속 받을 때 () 생성자 빼먹지 않도록 조심!
// 생성자에 Context와 MutableList 받아옴
class MyAdapter(var context:Context, var items:MutableList<Item>) : Adapter<MyAdapter.VH>() {
//이너클래스는 inner 키워드!
//ViewHolder 만들 때 constructor()로 View를 받는다
// itemView는 itemlayout의 최상위 카드뷰를 부른 것
inner class VH(itemView:View) : ViewHolder(itemView) {
val tvName:TextView by lazy { itemView.findViewById(R.id.tv_name) }
val tvMsg:TextView by lazy { itemView.findViewById(R.id.tv_msg) }
val iv:ImageView by lazy { itemView.findViewById(R.id.iv) }
}
//아이템뷰 레이아웃 불러오기
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
var itemView:View = LayoutInflater.from(context).inflate(R.layout.recycler_item,parent,false)
return VH(itemView)
}
override fun onBindViewHolder(holder: VH, position: Int) {
//var item:Item = items.get(position)
//코틀린은 리스의 get() set() 대신에 배열처럼 [] - 인덱스 번호 를 권장
var item:Item = items[position]
holder.tvName.setText(item.name)
//코틀린은 get/ set권장하지 않음
holder.tvMsg.text = item.msg
Glide.with(context).load(item.imgId).into(holder.iv)
//아이템 뷰를 클릭했을 때 화면 이동
// itemView : 아이템 뷰가 원래 부터 가지고 있는 멤버
holder.itemView.setOnClickListener {
val intent = Intent(context, ItemDetailActivity::class.java)
intent.putExtra("name", item.name)
intent.putExtra("msg", item.msg)
intent.putExtra("imgId", item.imgId)
///////////////////장면 전환 효과 만들기/////////////////////////////
//액티비티 전환시에 뷰들에 효과 주기 . ActivityOptions
// options 을 줄 떄 번들(꾸러미)로 만들어서 줘야함 옵션이 한두개가 아니니까
val options:ActivityOptionsCompat
= ActivityOptionsCompat.makeSceneTransitionAnimation(context as MainActivity,
Pair(holder.iv, "iii")
)
// context 사실 메인액티비티를 전달받은 것임 : 형변환 연산자 as로 메인액티비티임을 알려준다
//Pair() 객체 - 리사이클러뷰의 선택된 item에게 별명 붙이고 전환될 뷰에도 같은 별명을 붙여 한 쌍으로 만든다
/////////////////////////////////////////////////////////////////////
context.startActivity(intent , options.toBundle())
}
}
// 함수 리턴 값 - 할당 연산자로 단순화
override fun getItemCount(): Int = items.size
}