뷰바인딩
- view binding은 이 findViewById를 대체할 수 있는 기능이다
- findViewById의 문제점
- Null 안정성: 개발자가 실수로 유효하지 않은 id를 사용하면 null 오류가 발생할 수 있다.
- Type 안정성: textView의 타입을 imageView라고 잘못 적어서 캐스팅하면 cast exception이 발생할 수 있다.
- 속도가 상대적으로 느리다.
- 사용법
- gradle 추가
// 안드로이드 스튜디오 4.0 이상 android { ... buildFeatures { viewBinding = true } } // 안드로이드 스튜디오 3.6 ~ 4.0(3.6 보다 낮으면 안 됨) android { ... viewBinding { enabled true } }- 액티비티
2-1. [목차 1-2]에서 보여주었던 클래스를 만들기 위하여 위와 같이 적어준다.
inflate는 xml에 있는 뷰를 객체화해준다고 생각하면 된다.
2-2. 원래는 R.layout.activity_main을 넘겨주지만
이번에는 우리가 생성한 루트 뷰를 넘겨준다.
2-3. 바인딩된 객체 안에 있는 name, phone, address에 접근하여 사용하면 끝!class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.textView.text = "안녕" } }
Activity 이름 Binding Class 이름 MainActivity ActivityMainBinding HelloActivity ActivityHelloBinding XXXActivity ActivityXXXBinding - 프래그먼트
3-1 다른 건 다 똑같은데 다른 점이
onDestroyView에서 binding에 null을 집어넣어 준다는 것
why? : 프래그먼트은 뷰보다 오래갑니다.
프래그먼트의 onDestroyView() 메서드에서 바인딩 클래스 인스턴스에 대한 참조를 모두 정리해야 합니다.class BlankFragment : Fragment() { private var _binding: FragmentBlankBinding? = null //여기!! // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentBlankBinding.inflate(inflater, container, false) val view = binding.root return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.textView.text = "안녕" } override fun onDestroyView() { super.onDestroyView() _binding = null } }
AdapterView (ListView, GridView)
어댑터 뷰(AdapterView) : 여러 개의 항목을 다양한 형식으로 나열하고 선택 할 수 있는 기능을 제공하는 뷰
- 리스트 뷰(ListView) : 항목을 수직을 나열시키는 방식
- 그리드 뷰(GridView) : 항목을 격자 형태로 나열시키는 방식
- 어댑터(Adapter) : 데이터를 관리하며 데이터 원본과 어댑터뷰(ListView, GridView) 사이의 중계 역할
class [어댑터용도]Adapter(val post: MutableList<데이터 클래스>) : RecyclerView.Adapter<PostAdapter.Holder>() {
//RecyclerView = 어댑터 붙일 뷰의 종류
fun replaseDecimalFormat(num: Int): String {//1000자리 쉼표 표시 펑션(선택옵션)
return DecimalFormat("#,###").format(num).toString()
}
interface ItemClick {//어댑터 사용시 강제할 펑션 정의(선택옵션)
fun onClick(view: View, postion: Int)
fun onPressed(view: View, postion: 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) {
Log.d("onBindViewHolder", position.toString())
holder.itemView.setOnClickListener { itemClick?.onClick(it, position) }
holder.itemView.setOnLongClickListener { itemClick?.onPressed(it, position); true }
holder.postImageView.setImageResource(post[position].picture)
holder.postImageView.clipToOutline = true
holder.title.text = post[position].title
holder.address.text = post[position].address
holder.cost.text = replaseDecimalFormat(post[position].cost) + "원"
holder.chat.text = post[position].chat.toString()
holder.like.text = post[position].like.toString()
if (post[position].isLike) {
holder.isike.setImageResource(R.drawable.full_heart)
} else {
holder.isike.setImageResource(R.drawable.heart)
}
}
override fun getItemId(position: Int): Long {//아이템 아이디 가져옴(자동생성)
return position.toLong()
}
override fun getItemCount(): Int {//아이템 갯수 샘(자동생성)
return post.size
}
//홀더 정의
inner class Holder(val binding: ItemRecyclerviewBinding) :
RecyclerView.ViewHolder(binding.root) {
// 여기에 필요한 다른 뷰를 추가 가능
val postImageView = binding.ivItemPic
val title = binding.tvItemTitle
val address = binding.tvItemAddress
val cost = binding.tvItemCost
val chat = binding.tvChatNum
val like = binding.tvLikeNum
val isike = binding.ivItemLike
}
}
+ 추가로 알게된 용어
