처음 배울 때는 Scroll View와 LinearLayout을 이용하여 Data List를 보여주었다.

나쁘지 않다 생각했으나 아무것도 모를 때이기에 그렇게 생각했다ㅋㅋ

 

여기서 한 단계 더 배워 Adapter View를 배워서 ListView와 GridView를 통해 아이템들을 보여주었다.

그래 이거지,, 동적으로 추가도 가능해지며 좀 더 그럴 듯 해보였다.

그 전에 스크롤 뷰에서 아이템의 위치 값을 이용하기 위해 intent로 쌩쇼를 한게 부끄러울 정도다.

하지만 이런 ListView와 GridView도 문제점을 가지고 있었다.

Data가 100개, 1000개, 10000개라면?? 혹은 Data가 동적으로 계속 변경된다면?

 

앱은 과부하되어 실행되지 않을 수 있겠다...

 

왜냐하면 기존의 ListView나 GridView는 밑으로 스크롤 할 때마다 위에 있던 아이템은 삭제되고, 맨 아래의 아이템은 생성 되길 반복하기 때문이다. 한 마디로 비효율적으로 일을 하는 형태이다.

 

이런 단점을 보완하기 위해 나온 이름하야 리사이클러 뷰!!

RecyclerView란?

리사이클러 뷰는 현재 현업에서 리스트 형태의 Data를 표시하는데 무조건 쓰이는 가장 최신의 위젯이다.

아래 그림과 같이 리스트를 스크롤 할 때, 위에 있던 아이템은 재활용 돼서 아래로 이동하여 재사용 한다.

많은 아이템을 효율적으로 관리하고 보여주는 역할을 한다.

 

Recycler View를 사용할 땐 3가지가 필요하다.

 

1. LayoutManager

  • LayoutManager는 RecyclerView 내부의 아이템들이 어떻게 배치될지를 결정한다.
  • 기본 제공되는 **LayoutManager**로는 LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager 등이 있다.

recyclerView.layoutManager = LinearLayoutManager(this) // 수직 리스트를 위한 LinearLayoutManager
// 또는
recyclerView.layoutManager = GridLayoutManager(this, 2) // 2열 그리드를 위한 GridLayoutManager

 

2. RecyclerView.Adapter

  • RecyclerView에 표시될 데이터와 해당 데이터를 보여줄 ViewHolder를 연결한다.
  • Adapter는 데이터셋의 변경 사항을 RecyclerView에 알리고, 데이터를 기반으로 뷰를 생성합니다.
class MyAdapter(private val dataList: MutableList<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.textView)
        val thumbnailView: ImageView = view.findViewById(R.id.thumbnail)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
        return MyViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        Log.d("RecycleView", "onBindViewHolder :$position")
        holder.textView.text = dataList[position]
        holder.thumbnailView.setImageUri(dataList[position].thubnailUrl)
    }

    override fun getItemCount() = dataList.size

    // 데이터 추가
    fun addItem(data: String) {
        dataList.add(data)
        notifyItemInserted(dataList.size - 1)
    }

    // 데이터 삭제
    fun removeItem(position: Int) {
        if (position < dataList.size) {
            dataList.removeAt(position)
            notifyItemRemoved(position)
        }
    }
}

3. ViewHolder

  • RecyclerView의 개별 아이템 뷰를 위한 객체이다.
  • 아이템 뷰의 모든 서브 뷰를 담고 있어 재사용과 성능 최적화에 도움을 준다.
inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val thumbnailView: ImageView = view.findViewById(R.id.thumbnailView)
        val titleView: TextView = view.findViewById(R.id.titleView)
        // 여기에 필요한 다른 뷰를 추가할 수 있다.
    }

프로젝트를 할 때 마다 기본 버튼을 쓰니 튜터님들께서 디자인쪽으로 신경을 좀 쓰는게 좋을 것 같다는 피드백을 주셨다.

 

색이나 폰트 크기 등에 조금만 신경을 써도 생각보다 미적으로 보기가 편하긴 하다.

 

그래서 버튼에 조금이나마 디자인적인 요소를 넣을 수 있는 방법과 색상 변경이 안될 때 대처 방법을 소개한다.

 

간단하다.

 

 

이런 심플한 버튼을 하나 만든다.

 

그리고 drawable에서 drawable Resource File을 하나 만들어준다. root element에는 shape을 넣어주면 된다.

 

 

그리고 안에 내용물들을 채워주면 되는데 각각 태그들의 설명을 하자면.

 

solid 에서는 color를 통해 배경색을 지정해준다.

 

corners 에서는 radius를 통해 모서리부분에 대한 라운드 처리를 해준다.

 

size에서는 width와 height을 통해 사이즈를 지정해준다. (동일한 버튼이 여러개라면 이곳에서 지정해주는게 관리포인트를 줄일 수 있다.)

 

stroke에서는 width로 테두리 너비를 지정하고 color로 테두리 색을 지정해준다.

 

다 코드를 쳐주고 돌아와서 

android:background="@drawable/chat_background_pumpkin"

 

백그라운드에 넣어주면 버튼 디자인이 입혀지게된다.

 

그.런.데 여기서 안되는 나같은 사람도 있을 수 있을 것이다.

 

다 꾸며놨는데 디자인이 적용이 안되는 상황이 발생해버렸다.

 

하지만 해결방법은 너무나 간단했다.

 

<Button
    android:id="@+id/detail_item_chat"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="15dp"
    android:background="@drawable/chat_background_pumpkin"
    android:text="채팅하기"
    android:textSize="17sp"
    android:textStyle="bold"
    android:textColor="@color/white"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

 

기존에 이렇게 되어잇던 버튼 태그를..

 

<android.widget.Button

 

이런식으로 바꿔주면 적용이 된다.

 

https://school.programmers.co.kr/learn/courses/30/lessons/172928

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

class Solution {
    fun solution(park: Array<String>, routes: Array<String>): IntArray {
        var answer: IntArray = intArrayOf(0,0)
        var command = HashMap<String, IntArray>()
        
        command.put("E", intArrayOf(0,1))
        command.put("W", intArrayOf(0,-1))
        command.put("S", intArrayOf(1,0))
        command.put("N", intArrayOf(-1,0))
        
        for (i in 0 .. park.size-1) {
            for (j in 0 .. park[i].length-1) {
                if (park[i][j] == 'S') {
                    answer = intArrayOf(i,j)
                }
            }
        }
        
        routes.forEach {
            var direction = command[it.split(" ")[0]]
            var count = it.split(" ")[1].toInt()
            var row = answer[0]
            var column = answer[1]
            
            for (i in 1..count) {
                row += direction!![0]
                column += direction!![1]
                if(row < 0 || column < 0 || row >= park.size || column >= park[0].length || park[row][column] == 'X') {
                    row = answer[0]
                    column = answer[1]
                    break
                }
            }
            answer[0] = row
            answer[1] = column
        }
        return answer
    }
}

 

 

fun solution(park: Array<String>, routes: Array<String>): IntArray {
        var currY = 0
        var currX = 0
        val moveY = mapOf('N' to -1, 'S' to 1, 'W' to 0, 'E' to 0)
        val moveX = mapOf('N' to 0, 'S' to 0, 'W' to -1, 'E' to 1)

        for (i in park.indices) {
            for (j in park[i].indices) {
                if (park[i][j] == 'S') {
                    currY = i
                    currX = j
                }
            }
        }

        for (route in routes) {
            val direction = route[0]
            val step = route.substring(2).toInt()
            var maxStep = step

            for (i in 1..step) {
                val newY = currY + moveY[direction]!! * i
                val newX = currX + moveX[direction]!! * i

                if (newY < 0 || newX < 0 ||newY >= park.size || newX >= park[0].length || park[newY][newX] == 'X') {
                    maxStep = 0
                    break
                }
            }

            currY += moveY[direction]!! * maxStep
            currX += moveX[direction]!! * maxStep
        }

        return intArrayOf(currY, currX)
    }

 

https://school.programmers.co.kr/learn/courses/30/lessons/178871

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

 

class Solution {
    fun solution(players: Array<String>, callings: Array<String>): Array<String> {
        val rankMap = mutableMapOf<String, Int>()
        
        players.forEachIndexed { index, player ->
            rankMap[player] = index
        }
        
        callings.forEachIndexed { index, player ->
            val calledPlayerRank = rankMap[player] ?: 0
            val frontPlayer = players[calledPlayerRank - 1]
            
            players[calledPlayerRank -1 ] = player
            players[calledPlayerRank] = frontPlayer
            
            rankMap[player] = calledPlayerRank - 1
            rankMap[frontPlayer] = calledPlayerRank
        }
        return players
    }
}

 

rankMap에 {mumu=0, soe=1, poe=2, kai=3, mine=4} 이런 형식으로 플레이어와 그 인덱스 값을 저장해준다. 

 

이후 calling된 선수의 랭크를 통해 players안에 인덱스 값을 앞선수와 바꿔준다.

 

그리고 rankMap의 값도 변경시켜준다.

 

처음 temp에 서로 인덱스값을 바꿔주는 작업으로 접근했는데 시간 초과가 걸려 hashMap을 통해 검색 시간을 단축시켜서 해결하였다.

https://school.programmers.co.kr/learn/courses/30/lessons/150370

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

class Solution {
     fun solution(today: String, terms: Array<String>, privacies: Array<String>): IntArray {
        val answer = mutableListOf<Int>()
        val term = mutableMapOf<String, Int>()
        val todayInt = today.replace(".", "").toInt()

        terms.map {
            val (type, date) = it.split(" ")
            term[type] = date.toInt()
        }

        privacies.mapIndexed { i, it ->
            val (date, type) = it.split(" ")
            var year = term[type]!! / 12
            if (year < 1)
                year = 0

            val month = term[type]!! % 12
            val expiredDate = (date.replace(".", "").toInt() + year * 10000 + month * 100).toString()

            var temp = expiredDate.toInt()
            if (expiredDate[4].toString().toInt() * 10 + expiredDate[5].toString().toInt() > 12) {
                temp -= 1200
                temp += 10000
            }

            if (temp <= todayInt) {
                answer.add(i + 1)
            }
        }
        return answer.toIntArray()
    }
}

 

today 날짜를 점을 뺀 Int형식으로 날짜를 저장한다.

term 에 terms의 타입과 개월 수를 Map 형식으로 { A=6, B=12, C=3 } 이런식으로 저장한다.

 

이 후 유효기간의 소멸날짜를 구하기 위해 연수와 개월 수를 구하여 expiredDate에 저장한다.

월 수가 12를 초과했을 경우에 올림 계산을 위해 temp를 통해 연산을 해주고 temp와 오늘 날짜를 비교하여 오늘 날짜가 더 크다면 answer에 담아준다.

 

 

 

 

https://school.programmers.co.kr/learn/courses/30/lessons/161990

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

class Solution {
    fun solution(wallpaper: Array<String>): IntArray {
        var hashIndex = ArrayList<IntArray>()
        
        wallpaper.forEachIndexed { index, i ->
            i.forEachIndexed { index2, j ->
                if ( j == '#') {
                    hashIndex.add(intArrayOf(index, index2))
                }
            }
        }
        var minX = hashIndex.minOf{it[0]}
        var minY = hashIndex.minOf{it[1]}
        var maxX = hashIndex.maxOf{it[0]} + 1
        var maxY = hashIndex.maxOf{it[1]} + 1
        
        return intArrayOf(minX, minY, maxX, maxY)
    }
}

 

모든 #의 인덱스를 배열리스트에 넣어주고 X,Y좌표에서 최솟값과 최대값을 뽑아주면 되는 문제였다.

 

이중for문이어서 조금 걱정했으나 테스트케이스의 길이가 얼마 되지 않아 시간도 짧게 나왔다.

 

+ Recent posts