findViewById대체자이다. 번거롭게 위젯들을 하나하나씩 초기화 시켜주지 않아도 한번에 초기화시켜주는걸 도와준다.
findViewById와의 차이점? Null안전성, 타입 안전성이 있다는데 결론만 말하자면 더 안전하고, 더 사용하기 편리한 방법이 뷰 바인딩이다. 안쓸이유가 없다!
android{
...
// AndroidStudio 3.6 ~ 4.0
viewBinding{
enabled = true
}
// AndroidStudio 4.0 ~
buildFeatures{
viewBinding = true
}
}
나는 안드로이드 버전이 4.0이상인데 위에 방식을 사용해서 오류가났다.. 조심!
딱 4줄만 있으면 된다.
1.먼저 전역변수로 선언
private lateinit var binding: ActivityMainBinding
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
이렇게 해주면 모든 위젯들에 대해서 binding.(아이디이름)으로 대체해줄수 있다.
예를 들어 텍스트뷰하나와 버튼하나가 있고, 각각 아이디는 button, textView이다. 버튼 클릭시 택스트뷰의 내용을 "뷰바인딩 성공"이라고 바꾸기 위해서는 위 과정들을 거친후에
binding.button.setOnClickListener {
binding.textView.text = "바인딩 성공!"
}
간단하다.. 인생의 절반 손해봤다 애용할 예정이다..
+추가
private lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
원래 setContentView(R.layout.activity_main.xml)이런식으로 되어있는데 그대신 이렇게 넣어주면 더 간결하게 사용가능하다!
여기부터 앞으로는 위에서 배운 viewBinding을 활용할 예정이다.
<ListView
android:id="@+id/listView"
android:layout_width="409dp"
android:layout_height="710dp"/>
// 데이터 원본 준비
val items = arrayOf<String?>("item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8")
//어댑터 준비 (배열 객체 이용, simple_list_item_1 리소스 사용
val adapter = ArrayAdapter(this, R.layout.simple_list_item_1, items)
// 어댑터를 ListView 객체에 연결
binding.listView.adapter = adapter
이렇게하면
이렇게 리스트뷰를 생성할 수 있다.
<GridView
android:gravity="center"
android:id="@+id/gridView"
android:columnWidth="100dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:stretchMode="columnWidth"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
여기서 단순 값을 넣기, 사진넣기 두가지 종류로 나뉜다.
// 데이터 원본 준비
val items = arrayOf<String?>("item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8", "item5", "item6", "item7", "item8")
//어댑터 준비 (배열 객체 이용, simple_list_item_1 리소스 사용
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
// 어댑터를 GridView 객체에 연결
binding.gridView.adapter = adapter
//여기서 gridView는 xml에서 만든 gridView의 id값을 넣어준다.
이렇게하면 gridView형식으로 items배열에 담겨있는 값들이 나오게 된다.
binding.gridView.adapter = ImageAdapter()
사진을 넣을땐 추가로 ImageAdapter클래스를 만들어준다.
class ImageAdapter : BaseAdapter() {
override fun getCount(): Int {
return mThumbIds.size
}
override fun getItem(position: Int): Any {
return mThumbIds[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val imageView: ImageView
if (convertView == null) {
imageView = ImageView(parent!!.context)
imageView.layoutParams = AbsListView.LayoutParams(200, 200)
imageView.scaleType = ImageView.ScaleType.CENTER_CROP
imageView.setPadding(8, 8, 8, 8)
} else {
imageView = convertView as ImageView
}
imageView.setImageResource(mThumbIds.get(position))
return imageView
}
private val mThumbIds = arrayOf<Int>(
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7
)
}
이미지는 알아서 추가, BaseAdapter의 getCount(), getItem(), getItemId(), getView()메서드를 재정의함.
이렇게하면 gridView형식으로 이미지뷰에 담겨있는 값들이 나오게 된다.
아주 중요!!★★★★ 하다고 한다. 현업에서 많이 사용하는 뷰이다. 제대로 알아두자.
안드로이드 앱에서 리스트 형태의 데이터를 표시하는데 사용되는 위젯이다. 여러 아이템을 스크롤 가능한 리스트로 표현하며, 많은 아이템을 효율적으로 관리하고 보여준다
리스트뷰랑 차이점-
리스트뷰는 사용자가 스크롤 할때마다 위에있던 아이템은 삭제되고 아래의 아이템은 생성되는걸 반복한다. 하지만 리사이클러뷰는 스크롤 할때 위에있던 아이템은 재활용돼서 아래로 이동하여 재사용돼서 효율성이 높아진다.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
아이디와 함께 선언해준다.
//데이터 원본 준비
val dataList = mutableListOf<MyItem>()
dataList.add(MyItem(R.drawable.sample_0, "Bella", "1"))
dataList.add(MyItem(R.drawable.sample_1, "Charlie", "2"))
dataList.add(MyItem(R.drawable.sample_2, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.sample_3, "Duke", "1"))
dataList.add(MyItem(R.drawable.sample_4, "Max", "2"))
dataList.add(MyItem(R.drawable.sample_5, "Happy", "4"))
dataList.add(MyItem(R.drawable.sample_6, "Luna", "3"))
dataList.add(MyItem(R.drawable.sample_7, "Bob", "2"))
val adapter = MyAdapter(dataList)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
//여기부분은 뒤에서 다시나옴. 클릭시 이벤트
adapter.itemClick = object : MyAdapter.ItemClick{
override fun onClick(view: View, position: Int) {
val name: String = dataList[position].aName
Toast.makeText(this@MainActivity, "$name 선택!", Toast.LENGTH_SHORT).show()
}
}
여기서는 데이터를 직접 넣어줬다. MyItem이라는 Data Class를 만들어서 형식을 정해줬다.
data class MyItem(
val aIcon: Int,
val aName: String,
val aAge: String
)
데이터클래스를 만들어준다. 데이터를 넘길때 사진, 이름, 나이를 같이 넘기려고 해서 데이터타입을 지정해준거다.
어떤 형식으로 사진,이름,나이를 사용할지 xml파일을 하나더 만들어준다.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:id="@+id/iconItem"
android:padding="8dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:scaleType="centerInside"
android:src="@drawable/sample_1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textItem1"
android:textSize="20sp"
android:padding="4dp"
android:hint="Name"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textItem2"
android:textSize="16sp"
android:padding="4dp"
android:hint="Age"/>
</LinearLayout>
</LinearLayout>

이런 모양으로 저장했다.
MyAdapter를 만들어준다.
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {
interface ItemClick {
fun onClick(view : View, position : Int)
}
var itemClick : ItemClick? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnClickListener { //클릭이벤트추가부분
itemClick?.onClick(it, position)
/*
다른 액티비티로 이동하고싶다? 인텐트 사용해서 이동.
하지만 여기선 해당값의 이름을 그대로 mainActivity로 가져와서 사용하기때문에
ItemClick interface를 만들어서 사용중이다.
*/
//만약 클릭시 DetailActivity로 이동을 원한다? 아래내용.
//val intent = Intent(holder.itemView.context, DetailActivity::class.java)
//holder.itemView.context.startActivity(intent)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mItems.size
}
inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
val iconImageView = binding.iconItem
val name = binding.textItem1
val age = binding.textItem2
}
}
해당 뷰가 클릭시 다른페이지로 이동 혹은 다른 기능을 holder.itemView.setOnClickListener안에 넣어주면된다. 여기서는 화면전환을 하지 않고, 단순히 해당 값의 이름을 불러와 Toast메세지를 띄우기 위해서 ItemClick 인터페이스를 만들어주고 값을 다시 MainActivity로 넘겨주었다.
해당 부분은 MainActivity부분에 아래에 씌여있는 부분이다.
강아지 사진을 넣어야 하는데 편의상 아무 아이콘을 다운받았다 ㅎㅎ..
위 값들을 활용해서 다양한 시도를 해보면서 recyclerview를 익혀나가보면 될거같다.
위 부분에서 객체 클릭시 Toast메세지 띄우기 대신 DetailActivity로 이동하고, 해당 화면에서 선택된 사진,이름,나이를 가져와서 띄워보려고 한다.
<ImageView
android:layout_marginTop="100dp"
android:id="@+id/detail_img"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"/>
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="이름"
android:textSize="30sp"
android:textStyle="bold"
android:layout_gravity="center"/>
<TextView
android:id="@+id/text_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="나이"
android:textSize="30sp"
android:textStyle="bold"
android:layout_gravity="center"/>
화면 가운데에 사진을 띄울 이미지뷰하나, 이름과 나이를 띄울 텍스트뷰 두개를 만들어줬다.
@Parcelize
data class MyItem(
val aIcon: Int,
val aName: String,
val aAge: String
): Parcelable
값을 다음 액티비티로 보내주기 위해서 Parcelabel을 사용해줬다.
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnClickListener { //클릭이벤트추가부분
//메인액티비티로 값의 이름 가져가기
//itemClick?.onClick(it, position)
val intent = Intent(holder.itemView.context, DetailActivity::class.java).apply {
putExtra("data", mItems[position])
}.run{holder.itemView.context.startActivity(this)}
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
intent를 이용해서 DetailActivity로 이동하고 리스트에 있는 값을 putExtra를 이용해서 같이 넘겨준다.
intent를 생성할때는 'context'가 필요하며, 어댑터 클래스 자체는 이를 가지고있지 않기때문에 RecyclerView의 context를 사용하기 위해서 this대신 holder.itemView.context를 사용해줬다.
val selected = intent.getParcelableExtra<MyItem>("data")
val myImage = selected?.aIcon
val myName = selected?.aName
val myAge = selected?.aAge
if (myImage != null) {
binding.detailImg.setImageResource(myImage)
}
binding.textName.text = myName
binding.textAge.text = myAge
getParcelableExtra를 사용해서 키값을 받아와 이미지뷰와 텍스트뷰에 값을 넣어준다.
정말 자주 사용될 내용! 이다. 다음주까지 과제에도 필요한 내용이니 잘 봐두면 좋을거같다.