>알고리즘 문제
1. 삼각 달팽이
1)문제
정수 n이 매개변수로 주어집니다. 다음 그림과 같이 밑변의 길이와 높이가 n인 삼각형에서 맨 위 꼭짓점부터 반시계 방향으로 달팽이 채우기를 진행한 후, 첫 행부터 마지막 행까지 모두 순서대로 합친 새로운 배열을 return 하도록 solution 함수를 완성해주세요.

제한사항
- n은 1 이상 1,000 이하입니다.
2)솔루션
class Solution {
lateinit var arr:Array<IntArray>
fun solution(n: Int): IntArray {
arr=Array(n){IntArray(n)}
var top=0
var bot=n-1
val left=IntArray(n)
var right=IntArray(n){it}
val len=(n*(n+1))/2
var answer: IntArray = IntArray(len)
find(top,bot,left,right,1)
var idx=0
for(i in 0..n-1){
for(j in 0..n-1){
if(arr[i][j]==0) break
answer[idx++]=arr[i][j]
}
}
return answer
}
fun find(top:Int, bot:Int,left:IntArray,right:IntArray,idxinit:Int){
if(top>bot) return
var idx=idxinit
for(i in top..bot){
arr[i][left[i]++]=idx++
}
for(i in left[bot]..right[bot]){
arr[bot][i]=idx++
}
for(i in bot-1 downTo top+1){
arr[i][right[i]--]=idx++
}
find(top+2,bot-1,left,right,idx)
}
}
-직각삼각형 형태로 숫자를 문제에 나온 방식으로 채워넣음
-우변을 따라 숫자를 채운뒤에 우변은 한칸씩 당김
-아래변을 따라 숫자를 채운뒤에 아래 변을 한칸 위로 당긴다
-좌변을 따라 숫자를 채운뒤에 좌변을 한칸 당김
-마지막으로 높이를 두칸 내린뒤에 새로 생긴 직각삼각형을 따라서 다시 채움

-위 과정을 반복하면 정답을 얻을 수 있음
2. 블록 게임
1)문제 문제 설명
블록게임
프렌즈 블록이라는 신규 게임이 출시되었고, 어마어마한 상금이 걸린 이벤트 대회가 개최 되었다.
이 대회는 사람을 대신해서 플레이할 프로그램으로 참가해도 된다는 규정이 있어서, 게임 실력이 형편없는 프로도는 프로그램을 만들어서 참가하기로 결심하고 개발을 시작하였다.
프로도가 우승할 수 있도록 도와서 빠르고 정확한 프로그램을 작성해 보자.
게임규칙아래 그림과 같이 1×1 크기의 블록을 이어 붙여 만든 3 종류의 블록을 회전해서 총 12가지 모양의 블록을 만들 수 있다.

1 x 1 크기의 정사각형으로 이루어진 N x N 크기의 보드 위에 이 블록들이 배치된 채로 게임이 시작된다. (보드 위에 놓인 블록은 회전할 수 없다). 모든 블록은 블록을 구성하는 사각형들이 정확히 보드 위의 사각형에 맞도록 놓여있으며, 선 위에 걸치거나 보드를 벗어나게 놓여있는 경우는 없다.
플레이어는 위쪽에서 1 x 1 크기의 검은 블록을 떨어뜨려 쌓을 수 있다. 검은 블록은 항상 맵의 한 칸에 꽉 차게 떨어뜨려야 하며, 줄에 걸치면 안된다.
이때, 검은 블록과 기존에 놓인 블록을 합해 속이 꽉 채워진 직사각형을 만들 수 있다면 그 블록을 없앨 수 있다.
예를 들어 검은 블록을 떨어뜨려 아래와 같이 만들 경우 주황색 블록을 없앨 수 있다.

빨간 블록을 가로막던 주황색 블록이 없어졌으므로 다음과 같이 빨간 블록도 없앨 수 있다.

그러나 다른 블록들은 검은 블록을 떨어뜨려 직사각형으로 만들 수 없기 때문에 없앨 수 없다.
따라서 위 예시에서 없앨 수 있는 블록은 최대 2개이다.
보드 위에 놓인 블록의 상태가 담긴 2차원 배열 board가 주어질 때, 검은 블록을 떨어뜨려 없앨 수 있는 블록 개수의 최댓값을 구하라.
제한사항- board는 블록의 상태가 들어있는 N x N 크기 2차원 배열이다.
- N은 4 이상 50 이하다.
- board의 각 행의 원소는 0 이상 200 이하의 자연수이다.
- 0 은 빈 칸을 나타낸다.
- board에 놓여있는 각 블록은 숫자를 이용해 표현한다.
- 잘못된 블록 모양이 주어지는 경우는 없다.
- 모양에 관계 없이 서로 다른 블록은 서로 다른 숫자로 표현된다.
- 예를 들어 문제에 주어진 예시의 경우 다음과 같이 주어진다.
2)솔루션
class Solution {
fun solution(board: Array<IntArray>): Int {
var answer = 0
val head=Array(201){HashSet<Pair<Int,Int>>()}
for(i in board.indices){
for(j in board[0].indices){
if(board[i][j]!=0) head[board[i][j]]+=Pair(i,j)
}
}
val heads=HashSet<Int>()
val upper=IntArray(board[0].size)
for(i in board[0].indices){
for(j in board.indices){
if(board[j][i]!=0){
heads+=board[j][i]
upper[i]=j
break
}
}
}
while(true){
val removed=HashSet<Int>()
val added=HashSet<Int>()
for(i in heads){
val p=IntArray(8)
var idx=0
for(j in head[i]){
p[idx++]=j.first
p[idx++]=j.second
}
val blanks=blank(p)
blanks?.let{
var flag=true
//반환받은 빈공간이나 그 위에 블럭이 있는지 체크
for(j in it){
if(j.first>=upper[j.second]) flag=false
}
if(flag){
answer++
//블럭 제거 후 해당열 upper와 heads갱신
for((x,y) in head[i]){
board[x][y]=0
}
for((x,y) in head[i]){
for(k in board.indices){
if(board[k][y]!=0){
upper[y]=k
added+=board[k][y]
break
}
}
}
removed+=i
}
}
}
if(removed.size==0) break
//다른 head를 처리하면서 기존에 있으면서 사라져야 할 head가 added에 추가될 수 있음
//그때문에 removeAll을 후처리
heads.addAll(added)
heads.removeAll(removed)
}
return answer
}
//가능한 타입:13,14,22,23,31
//가능한 타입이면 빈공간을 반환, 아니면 널 반환
fun blank(p:IntArray):Array<Pair<Int,Int>>?{
//높이가 2가지 뿐이면 누운 타입
val w=intArrayOf(p[1],p[3],p[5],p[7])
val h=intArrayOf(p[0],p[2],p[4],p[6])
//누운 타입
if(h.distinct().size==2){
//아래를 보는 누운타입이면 널 반환
val up=h.minOrNull() as Int
val down=h.maxOrNull() as Int
var cnt=0
h.forEach{if(it==down) ++cnt}
if(cnt==1){
return null
}
//타입에 따라 빈공간 반환
val left=w.minOrNull() as Int
val right=w.maxOrNull() as Int
val center=w.filter{it!=left&&it!=right}[0]
val upidx=h.indexOf(up)
if(w[upidx]==left){
return arrayOf(Pair(up,right),Pair(up,center))
}else if(w[upidx]==right){
return arrayOf(Pair(up,left),Pair(up,center))
}else{
return arrayOf(Pair(up,left),Pair(up,right))
}
//선 타입
}else{
//튀어나온 부분이 중앙이나 위에 있으면 널 반환
val up=h.minOrNull() as Int
val down=h.maxOrNull() as Int
val center=h.filter{it!=up&&it!=down}[0]
var cntCenter=0
var cntUp=0
h.forEach{if(it==up) ++cntUp else if(it==center) ++cntCenter}
if(cntCenter==2||cntUp==2){
return null
}
//타입에 따라 빈공간 반환
val left=w.minOrNull() as Int
val right=w.maxOrNull() as Int
//좌측으로 튀어나온 경우
if(w.filter{it==left}.size==1){
return arrayOf(Pair(up,left),Pair(center,left))
//우측으로 튀어나온 경우
}else{
return arrayOf(Pair(up,right),Pair(center,right))
}
}
}
}
-단위블록: 정사각형 모양의 칸 / 블록: 같은 인덱스를 가진 단위블록끼리 모여 모양을 이루는 도형이라 하자
-우선 모든 블록을 head로 묶는다.
-처음에 모든 열을 체크해서 각 열 별로 가장 윗 단위블록과 head를 upper와 heads에 각각 저장해둔다.
-ㅡㅡㅡㅡㅡ이 아래 반복 ㅡㅡㅡㅡㅡ
-저장된 head들에 대해서 각각 해당 블록이 사라지기 위한 빈공간을 blank함수로 찾는다.
-단 blank함수 사용시 사라질 수 없는 블록에 대해서는 null를 반환받는다.
-upper를 체크하여 반환받은 빈 공간이나 그 위에 블록이 존재하는지 확인
-없으면 블록을 제거하고 answer++. 이후 블록이 사라진 열을 체크하여 새로 heads와 upper를 갱신한다.
- ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
-위 과정 반복을 하여 answer을 구한다.
>숙련 1주차 강의
1. 뷰 바인딩
<1>사용: findViewById를 대체
<2>장점
-널 안정성: 뷰가 아직 화면에 안 나타났는데 사용하려 할시 생기는 문제 예방
=>예시: 버튼이 생성되지 않았는데 버튼 사용하려는 경우
-타입 안정성: 잘못된 타입으로 사용되는것을 막음
=>예시: 이미지뷰에 텍스트를 설정하려 하는경우
<3>뷰 바인딩 설정 방법
-gradle설정
android{
...
// AndroidStudio 3.6 ~ 4.0
viewBinding{
enabled = true
}
// AndroidStudio 4.0 ~
buildFeatures{
viewBinding = true
}
}
-Activity설정
class MainAcitivity: AppCompatActivity(){
private lateinit var binding: ActivityMainBinding //lateinit으로 변수 설정
override fun onCreate(saveInstanceState: Bundle?){
super.onCreate(saveInstanceState)
//이 아래부분 추가 및 수정
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
}
-binding.root: root는 최상위 뷰를 말함. 즉 최상위뷰를 UI로 지정
-layoutInflater: 현재 액티비티 인스턴스에서 가져온 LayoutInflater 객체를 말함. Activity의 멤버.
<4>사용방법
binding.아이디.메소드
<5>바인딩 클래스 이름
-레이아웃 파일의 이름에 따라 달라짐
-예시:activity_main.xml 인 경우 ActivityMainBinding으로 이름이 지어짐
2. 어댑터뷰(리스트뷰, 그리드 뷰,리사이클러)
<1>어댑터 뷰
(1)어댑터 뷰란
-여러개 항목을 다양한 형식으로 나열 및 선택할 수 있도록 기능을 제공하는
-어댑터뷰는 표시할 항목 데이터를 어댑터라는 객체로 부터 공급받음
(2)어댑터
-데이터 관리: 데이터 원본과 어댑터 뷰 사이의 중계역할
-데이터를 항목에 표시하는 방법
[1]데이터 원본에 어댑터 설정 & 어댑터 뷰에 어댑터 설정
[2]getCount()로 데이터 항목 총 개수 반환
[3]getView()로 화면에 표시할 항목뷰를 얻고 표시
-사용자와 상호작용: 사용자가 항목 선택시 어댑터 뷰는 어댑터로부터 getItem(), getItemId(), getView()로 항목 정보를 받아와 항목선택 이벤트 처리기로 넘김
(3)어댑터 종류
-BaseAdapter: 어댑터 클래스의 공통구현, 사용자 정의 어댑터 구현시 사용
-ArrayAdapter: 객체나 리소스에 정의된 배열로부터 데이터 공급받음
-CursorAdapter: 데이터베이스로부터 데이터 공급받음
-SimpleAdapter: 데이터를 Map으로 관리, 데이터를 XML파일에 정의된 뷰에 킴

<2>리스트 뷰
(1)형태: 리스트 형식으로 항목이 나열되는 어댑터 뷰
(2)만들기
[1]xml에 리스트뷰 생성
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
[2]어댑터 생성
-형식: ArrayAdapter(Context context, int resource, T[] objects)
=>Context는 보통 this를 쓰면 되고, resource 항목으로 표시될 뷰의 아이디, object는 데이터 원본(보통 배열)

[3]어댑터 뷰와 어댑터 연결
bindging.listView.adapter = adapter
<3>그리드 뷰
(1)형태: 격자 형식으로 나타내지는 뷰
(2)만들기:
[1]xml에 그리드뷰 생성
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="100dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
-columnWidth: 그리드 항목당 폭 길이 설정
-numColumns="auto_fit": 열과 화면의 폭을 바탕으로 열의 개수를 자동 계산
=>""안에 숫자를 쓰면 숫자만큼 열의 개수가 정해짐
-verticalSpacing: 항목 간의 수직 간격 설정
-horizontalSpacing: 항목 간의 수평 간격 설
-stretchMode="columnWidth": 열이 뷰의 너비에 맞게 확장되는 방식으로
=>이경우 열의 크기가 고정되어 뷰 내부에 일정간격으로 나열시킴. 또한 이 속성을 사용하려면 columnWidth가 필요함
@stretchMode의 다른 값들
-spacingWidthUniform: 열간격을 균일하게 유지하며 열이 확장됨=>화면 너비에 상관없이 열 간격 고
-spacingWidth:열 사이 간격을 유지하며 열이 확장=>화면 너비에 따라 열 간격이 변함
[2]이후 과정은 listview와 완전히 동일
(3)이미지 그리드뷰 만들기
[1]그리드 뷰 생성
[2]커스텀 어댑터 클래스 생성
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
)
}
-getCount(): 항목의 총 개수
-getItem(): 항목 데이터
-getItemId(): 항목 데이터의 인덱스
-getView( position: Int, convertView: View?, parent: ViewGroup? ):
{1}position: 반환할 항목의 위치 인덱스
{2}convertView: 이전에 생성된 항목뷰. 처음만들어 지는 경우(convertView==null)라면 새로운 이미지뷰 객체를 만들고 크기, 스케일 타입, 패딩을 설정. 이전에 만들어졌다면 이를 재사용.
{3}parent:ViewGroup? :여기선 그리드 뷰를 나타냄
@ViewGroup을 상속받는 것으로는 리니어레이아웃, 컨스트레인트 레이아웃, 리사이클러뷰등이 있다
@ AbsListView.LayoutParams
- AbsListView는 리스트뷰와 그리드뷰의 부모 클래스
- AbsListView.LayoutParams는 해당 뷰들 내부 항목의 레이아웃 속성을 설정: 너비와 높이
@ ImageView(parent!!.context)
-이미지뷰가 리소스를 사용하기 위해서는 context가 필요. parent(뷰그룹)가 속한 context를 받아옴
@View.context
-뷰가 속한 context를 받아옴.
@context
-실행중인 액티비티 혹은 서비스
-뷰가 포함된 레이아웃이나 컨테이너의 context를 의미
(4)어댑터뷰 클릭 이벤트
binding.gridview.setOnItemClickListener{ parent, view, position, id ->
Toast.makeText(this@MainActivity,"" + (position + 1) + "번째 선택",
Toast.LENGTH_SHORT).show()
}
-parent: 클릭이 발생한 AdapterView
-view: 실제 클릭된 어댑터뷰 내의 view
-position: 어댑터내에서 클릭된 항목의 위치
-id: 클릭된 항목의 아이디
@ Annotation this@MainActivity
-this를 쓰면 자신이 포함된 객체를 나타낸다.
-단 객체가 중첩되어있는 경우 annotation을 이용하여 어느 객체를 가리키는지 명시할 수 있다.
-예시
class OuterClass {
inner class InnerClass {
fun doSomething() {
println(this@InnerClass) // InnerClass 인스턴스
println(this@OuterClass) // OuterClass 인스턴스
}
}
}
<4>리사이클러뷰
(1)리사이클러뷰란: 리스트형태 데이터를 표시하는 위젯
=>뷰를 재활용해서 사용하는 위젯
(2)리스트뷰와 리사이클러뷰의 차이
-리스트뷰는 스크롤마다 보이는 화면을 벗어난 아이템은 삭제하고 새로 보이는 아이템을 생성 반복=>성능이 안 좋음
=>100개 아이템에 대해 스크롤을 하면 100개를 전부 생성해야함
-리사이클러뷰는 스크롤시 화면에서 벗어난 아이템을 다시 스크롤방향에 가지고와 재활용=> 성능 비교적 우수
=>100개 아이템에 대해 스크롤하면 10개 정도 뷰를 만들어 재활용
(3)리사이클러뷰 사용
[1]Adapter: 데이터 테이블을 목록 형태로 보여주기위한 것으로 데이터와 리사이클러뷰 사이에 존재하는 객체
[2]ViewHolder: 화면에 표시될 데이터나 아이템을 저장하는 역할
=>리사이클러뷰는 스크롤해서 사라진 View를 재활용하기 위해 기억해야 하는데 ViewHolder가 그 역할을
(4)사용법(예제)
[1]메인 화면 레이아웃에 리사이클러뷰 위젯 생성
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
[2]리사이클러뷰 아이템을 담을 레이아웃 생성:이름은 item_recyclerview.xml
[3]어댑터 클래스 정의
-전체코드
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)
}
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
}
}
-클래스 상속
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>()
{1}리사이클러뷰의 어댑터를 상속받아 구현
{2}뷰홀더로 사용된것이 inner class인 Holder이므로 타입에 MyAdapter.Holder가 들어감
@inner class와 nested class
{1}nested class:
-클래스내에 클래스를 정의하면 기본적으로 중첩 클래스로 정의됨
-외부 클래스를 참조하지 않기 때문에 외부 클래스의 멤버를 사용할 수 없다.
-선언 및 사용: 외부클래스명.내부클래스명()
-예시: 이 경우에 오류가 남
class Outer{
val a=1
class Nested{
fun foo(){
println(a) //a가 Unresolved reference로 오류가 뜬다.
}
}
}
fun main(){
println(Outer.Nested().foo()) //에러!
}
{2}inner class
-앞에 inner을 추가해서 사용
-외부 클래스를 참조하기 때문에 외부 클래스의 멤버에 접근할 수 있다.
-선언 및 사용: 외부클래스명().내부클래스명()
=>외부클래스를 참조하고 있기때문에 먼저 외부클래스를 생성해 주어야 한다.
-예시
class Outer{
val a=1
inner class Inner{
fun foo(){
println(a) //오류 없음
}
}
}
fun main(){
println(Outer().Inner().foo()) // 1 출력
}
-뷰홀더
inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
val iconImageView = binding.iconItem
val name = binding.textItem1
val age = binding.textItem2
}
{1}뷰에 보여질 내용을 저장하는 역할
{2} 뷰 역할을 할 레이아웃이름이 item_recyclerview.xml이므로 바인딩 클래스 이름은 ItemRecyclerviewBinding
-뷰홀더 생성
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding)
}
{1}parent는 리사이클러뷰
{2}리사이클러뷰에 즉시 추가하지는 않고 최종적으로 뷰홀더를 생성시킴
@LayoutInflater
{1}inflate() 메소드를 통해 xml레이아웃 파일을 실제 뷰 객체로 변환함
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.my_layout, parentView, false)
-context는 fragment나 activity
-위의 코드에서 inflater가 R.layout.my_layout이란 xml파일을 실제 뷰 객체로 변환시켜(인플레이션) 인플레이션된 뷰를 parentView에 추가.
-이때 false는 부모에 즉시 추가하지는 않음을 나타낸다.
=>true면 즉시 추가된다.
{2}바인딩 클래스에서의 inflate
Binding.inflate(레이아웃인플레이터,부모뷰,bool)
-레이아웃 인플레이터(LayoutInflater.from(context)):
=>context는 인플레이션에 필요한 리소스 액세스(ex-inflate시 부모뷰를 참조하기 위함) 시스템 서비스 호출에 필요
=>즉 부모뷰가 포함된 context가 필요함. 그 때문에 위에서 뷰홀더 생성시 LayoutInflater가 parent의 context를 받은것
-부모뷰:바인딩클래스가 나타내는 뷰가 어느뷰에 추가될지 결정되는 것이 부모뷰
-bool:true면 즉시 추가되고 false면 즉시 추가하지는 않음을 나타냄.
@부모뷰와 bool부분이 생략되는 경우
-bool은 생략되면 자동으로 false처리 되지만 부모뷰 부분은 null처리됨.
-바인딩 클래스에서 inflate()는 inflate(layoutInflater)는 inflate( layoutInflater, null, false)로 처리되기 때문.
-이렇게 null처리된 것은 다른 뷰에 추가하거나 setContentView()로 직접 화면 설정 할 수 있다.
-뷰홀더와 데이터를 바인드
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnClickListener { //클릭이벤트추가부분
itemClick?.onClick(it, position)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
=>생성시킨 Holder를 받아와 각각의 데이터를 연결시킴
-아이템의 아이디와 총 개수
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mItems.size
}
@ init { setHasStableIds(true) }
-adapter안에 넣어서 구현
-동일 id의 아이템에 대해서는 onBindViewHolder가 호출되지않고 기존에 생성된 뷰홀더를 사용한다
-위에서 id를 필요로 하기 때문에 getItemId를 오버라이드 해줘야함.(단 각 아이템이 고유한 아이디를 가질 수 있도록)
-클릭 이벤트 구현
interface ItemClick {
fun onClick(view : View, position : Int)
}
var itemClick : ItemClick? = null
=>클릭에 대한 인터페이스 생성
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()
}
}
{1}Main에서 클릭에 대한 인터페이스를 상속받는 오브젝트를 생성
{2}클릭 이벤트에 대한 내용을 오버라이드해서 어댑터 내의 itemClick 프로퍼티에 할당
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnClickListener { //클릭이벤트추가부분
itemClick?.onClick(it, position)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
=>onBindViewHolder에서 클릭이벤트 감지시 itemClick에 저장된 클릭 이벤트를 실행시킴
-MainActivity
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
'코틀린-안드로이드' 카테고리의 다른 글
50일차)알고리즘 문제(두 큐 합 같게 만들기), 숙련 1주차 강의(프래그먼트 데이터 전달, 다이얼로그, 알림) (1) | 2024.07.12 |
---|---|
49일차)알고리즘 문제, 숙련 1주차 강의(프레그먼트) (0) | 2024.07.11 |
47일차)알고리즘 문제(큰 수 만들기), 챌린지반 3주차 강의(디자인 패턴, MVVM), 챌린지반 과제 (1) | 2024.07.09 |
46일차)알고리즘 문제(경사로의 개수, 택배 상자), 팀프로젝트 마무리 및 발표, 비행기 전광판 과제 (0) | 2024.07.08 |
45일차)알고리즘 문제(동굴 탐험) (0) | 2024.07.07 |