List View 구성하기

변현섭·2023년 8월 10일
0
post-thumbnail

이번 포스팅에서는 조금 어려우면서도, 매우 중요한 리스트 뷰에 대해 다뤄볼 것입니다. 응원의 메시지를 보여주는 어플리케이션을 만들어보면서, 리스트 뷰를 구성하는 방법에 대해 알아보도록 하겠습니다.

1. 레이아웃 구성하기

1) 홈 화면

① cheerup message라는 이름으로 프로젝트를 생성한다.

② 이번 포스팅에서는 activity_main.xml 파일의 default Layout인 Constraint Layout을 그대로 사용할 것이다. 배경색을 연한 노란색으로 지정해주자.

android:background="#e9ec69"

③ 전체 보기 버튼을 생성한다. 버튼의 색상은 주황색으로 하겠다.

<Button
	android:layout_width="200dp"
    android:layout_height="50dp"
    android:background="#ff7f00"
    android:text="전체 보기"/>
  • 버튼의 색상이 적용되지 않을 경우, res > values > themes > (night 안 쓰여있는) themes.xml 파일의 3번째 줄을 아래와 같이 수정해야 한다. name 속성에는 Base.Theme.{프로젝트 이름(Camel Case)}을 넣어야 한다.
<style name="Base.Theme.CheerupMessage" parent="Theme.AppCompat.Light">

④ Constraint Layout은 상대적으로 위치를 설정해야하기 때문에 현재 에러가 나고 있을 것이다.

  • 디자인 화면에서 버튼을 클릭하면, 동그라미가 나오는데 이 동그라미를 Top, Left, Bottom, Right에 가져다 대면 그 위치를 기준으로 버튼을 배치할 수 있다. Top과 Right에 가져다대보자.
  • 이제 버튼을 움직이면, Top과 Right를 기준으로 몇 dp 떨어져있는지로 버튼을 배치할 수 있게 된다.

<Button
	android:layout_width="200dp"
    android:layout_height="50dp"
    android:layout_marginTop="24dp"
    android:layout_marginEnd="24dp"
    android:background="#ff7f00"
    android:text="전체 보기"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

⑤ 응원 메시지가 화면의 가운데에 나타나도록 설정한다. 가운데에 나타나게 하는 방법은 동그라미를 Top, Left, Bottom, Right 모두에 연결하는 것이다.

<TextView
	android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="당신만을 위한 응원의 메시지"
    android:textColor="#000000"
    android:textSize="30sp"
    android:gravity="center"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.499"/>

⑥ 커스텀 폰트를 적용한다.

  • 아래의 링크를 통해 배달의 민족 주아체 또는 도현체를 다운로드 받는다.
    >> 주아체 다운로드
    >> 도현체 다운로드
  • 다운 받은 파일을 res 디렉토리 하위에 font 패키지를 만들어 그 안에 넣는다. 대문자를 소문자로 변경해주어야 빨간줄이 사라진다.

  • TextView 태그 안에 아래의 내용만 추가해주면 폰트가 적용된다.
android:fontFamily="@font/bmjua"

2. 전체 보기 버튼으로 화면 전환하기

① MessageListActivity를 생성한다.

② gradle 파일에 아래의 코드를 추가한 후, Sync Now를 클릭한다.

buildFeatures{
	dataBinding = true
}

③ activity_main.xml의 코드를 layout 태그로 감싼다.

④ 데이터 바인딩을 사용하기 위해 버튼의 id를 지정해주자.

<Button
	android:id="@+id/list"

⑤ MainActivity 파일에 데이터바인딩을 설정하고, 버튼에 대한 클릭 이벤트로 화면 전환 기능을 추가한다.

private lateinit var binding : ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.list.setOnClickListener {
    	val intent = Intent(this, MessageListActivity::class.java)
        startActivity(intent)
    }
}

⑥ 코드를 실행해보면 애플리케이션 상단 바에 애플리케이션의 제목이 나오는데, 이를 없애려면 themes.xml 파일(버튼 색상을 적용하기 위해 사용했던 xml 파일)에 "windowNoTitle" 속성 값을 추가해야 한다.

<style name="Base.Theme.CheerupMessage" parent="Theme.AppCompat.Light">
        <item name="windowNoTitle">true</item>

3. 메인 화면에서 랜덤 응원 메시지 보여주기

① MainActivity 파일에 응원의 메시지 목록을 작성한다.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val sentenceList = mutableListOf<String>()
    sentenceList.add("자신의 행동에 대해 너무 소심하고 까다롭게 고민하지 말라. " +
                "모든 인생은 실험이다. 더 많이 실험할수록 더 나아진다.")
    sentenceList.add("명성을 쌓는데는 20년이라는 세월이 걸리며 " +
                "명성을 망가뜨리는데는 채 5분도 걸리지 않는다. " +
                "그것을 명심한다면 당신의 행동이 달라질 것이다.")
    sentenceList.add("성공은 영원하지 않고 실패는 치명적이지 않다.")
    sentenceList.add("멈추지 말고 한 가지 목표를 향해 달려가라. " +
                "그것이 성공의 비결이다")
    sentenceList.add("당신의 행복은 무엇이 당신의 영혼을 노래하게 하는가에 의해 결정된다.")
    sentenceList.add("사람이 인생에서 가장 후회하는 어리석은 행동은 " +
                "기회를 놓치는 것이다.")

    val adapter = ListViewAdapter(sentenceList)
    val listview = findViewById<ListView>(R.id.messageListView)
    listview.adapter = adapter
}

② 데이터 바인딩을 위해 activity_main.xml의 TextView에 id를 넣어주어야 한다.

<TextView
	android:id="@+id/message"

③ MainActivity 파일에 아래의 내용만 추가하면, TextView의 text 값이 sentenceList의 무작위 텍스트 값으로 설정될 것이다.

binding.message.setText(sentenceList.random())

4. 리스트 뷰 구성하기

리스트 뷰를 구성하기 위해선 아래의 3가지의 파일이 필요하다.

  • listview_item.xml 파일
  • Adapter 클래스
  • ListView를 적용할 Activity 파일

1) listview_item.xml

① 전체 보기에서 리스트 뷰로 메시지를 보여주기 위해 layout 디렉토리에 우클릭 > Layout Resource File 을 클릭한 후, File Name을 listview_item으로 설정하고 OK를 누른다.

② listview_item.xml 파일의 레이아웃을 Linear Layout으로 변경한다.

③ Linear Layout 안에 TextView 태그를 추가한다.

<TextView
	android:id="@+id/listView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="15sp"
    android:fontFamily="@font/bmjua"
    android:text="TextArea"
    android:layout_margin="10dp"/>

2) Adapter

① MainActivity가 위치한 디폴트 디렉토리에 우클릭 > New > Kotlin Class/File을 클릭한다. ListViewAdapter라는 클래스를 생성한다.

② 아래와 같이 입력한 후, 우클릭 > Show Context Actions > Implement members를 클릭하면 자동으로 override 목록이 추가된다.

class ListViewAdapter(val dataList : MutableList<String>) : BaseAdapter() {
	
}
  • 생성자 파라미터로 dataList를 받고 있다.
  • MutableList는 수정 가능한 문자열 리스트를 의미한다.

③ override 메서드를 정의한다.

override fun getCount(): Int {
	return dataList.size
}

override fun getItem(position: Int): Any {
    return dataList[position]
}

override fun getItemId(position: Int): Long {
    return position.toLong()
}

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
	var convertView = convertView

    if(convertView == null) {
    	convertView = LayoutInflater.from(parent?.context).inflate(R.layout.listview_item, parent, false)
    }
    val listViewText = convertView?.findViewById<TextView>(R.id.listView)
    listViewText!!.text=dataList[position]
    return convertView!!
}
  • getCount(): 리스트뷰에 표시할 아이템의 개수를 반환하는 메서드로, 거의 고정적으로 return dataList.size를 사용한다.
  • getItem(): 리스트에서 해당 인덱스의 값을 가져오는 메서드로, 거의 고정적으로 return dataList[postion]을 사용한다.
  • getItemId(): 아이템의 ID를 반환하는 메서드로, 여기서는 아이템의 인덱스를 그대로 ID로 사용하고 있다.
  • getView()
    • 아이템의 뷰를 생성하고 반환하는 메서드이다.
    • convertView: 재활용을 위한 뷰 객체로, null인 경우에만 새로운 뷰를 생성한다.
    • parent: 아이템 뷰가 속하는 부모 뷰 그룹을 가리킨다.
    • LayoutInflater: listview_item 레이아웃을 inflate하여 뷰를 생성한다. 거의 형식이 고정적이다.
    • convertView?.findViewById: id가 listView인 TextView를 저장하는 변수이다.
    • listViewText!!.text=dataList[position]: dataList에서 인덱스에 해당하는 데이터를 저장한다. 즉, 아이템 뷰 내의 특정 TextView에 데이터를 설정하는 작업을 수행한다.
    • 참고로, ?는 null일 수 있는 변수를 의미하고, !!는 null이 아님을 확신하는 변수를 의미한다.

※ ConvertView
안드로이드의 ListView나 RecyclerView에서 아이템 뷰를 재활용하기 위해 사용하는 뷰 객체이다. 많은 아이템을 표시해야 하는 상황에서 모든 아이템에 대해 새로운 뷰를 생성하는 것은 매우 비효율적이기 때문에 convertView를 활용하여 이미 생성된 뷰를 재사용한다. 즉, 새로운 아이템이 추가될 때마다 기존 뷰에서 데이터 값만 새롭게 setting하는 방식인 것이다.

※ Inflate
inflate는 안드로이드에서 xml 레이아웃 파일을 실제 뷰 객체로 변환하는 과정을 의미한다. 다시 말해 xml 레이아웃 파일에 정의된 디자인 요소들을 실제 화면에 보이는 뷰 객체로 만드는 작업인 것이다. 스크롤로 ListView에서 화면 밖의 새 아이템을 가져올 때, getView를 호출하는데, 첫 호출 시 아이템의 개수만큼 convertView를 생성하고, 이후 호출에선 convertView를 재활용한다. 즉, convertView가 null일 경우에는 inflate하고, null이 아닐 경우, inflate 하는 대신 기존 뷰에 데이터만 새로 set한다. 만약 view를 각각의 아이템마다 매번 inflate 한다면 성능과 효율이 저하되고, 메모리 부담이 가중될 것이다.

3) ListView를 적용할 Activity 파일

① activity_message_list.xml 파일에 ListView 태그를 추가한다.

<ListView
	android:id="@+id/messageListView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

② MessageListActivity 파일에 아래와 같이 코드를 작성한다.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_message_list)
	
    val sentenceList = mutableListOf<String>()
        sentenceList.add("자신의 행동에 대해 너무 소심하고 까다롭게 고민하지 말라. " +
                "모든 인생은 실험이다. 더 많이 실험할수록 더 나아진다.")
        sentenceList.add("명성을 쌓는데는 20년이라는 세월이 걸리며 " +
                "명성을 망가뜨리는데는 채 5분도 걸리지 않는다. " +
                "그것을 명심한다면 당신의 행동이 달라질 것이다.")
        sentenceList.add("성공은 영원하지 않고 실패는 치명적이지 않다.")
        sentenceList.add("멈추지 말고 한 가지 목표를 향해 달려가라. " +
                "그것이 성공의 비결이다")
        sentenceList.add("당신의 행복은 무엇이 당신의 영혼을 노래하게 하는가에 의해 결정된다.")
        sentenceList.add("사람이 인생에서 가장 후회하는 어리석은 행동은 " +
                "기회를 놓치는 것이다.")

        val adapter = ListViewAdapter(sentenceList)
        val listview = findViewById<ListView>(R.id.messageListView)

        listview.adapter = adapter
    }
}
  • ListViewAdapter 클래스의 입력인자로 sentenceList를 넣고, ListView의 adapter로 설정한다.

5. 리스트 뷰 Review

ListView는 한번에 이해하기 어렵기 때문에, 다시 한번 구성 순서에 대해 이야기하겠다.

① listview_item.xml 파일을 layout 디렉토리 하위에 추가

② LinearLayout으로 변경한 뒤, TextView 또는 ImageView 태그 추가

  • id, 폰트의 크기, 적용할 폰트 모두 이 태그 안에 입력

③ Adapter 클래스 생성 후 아래의 내용을 입력, Generics는 상황에 맞게 변경

class ListViewAdapter(val dataList : MutableList<String>) : BaseAdapter() {
}

④ 메서드 오버라이딩

⑤ ListView를 적용할 Activity의 xml 파일에 ListView 태그 추가

  • id 속성 입력

⑥ ListView를 적용할 Activity 파일에 mutableListOf로 리스트 생성 후 add로 items 추가

⑦ mutableListOf로 생성한 List를 Adapter의 입력인자로 넣고, 방금 만든 ListView의 adapter로 넘겨준다. 아래의 코드 정도는 외워두는게 좋다.

val adapter = ListViewAdapter(sentenceList)
val listview = findViewById<ListView>(R.id.messageListView)
listview.adapter = adapter
profile
Java Spring, Android Kotlin, Node.js, ML/DL 개발을 공부하는 인하대학교 정보통신공학과 학생입니다.

1개의 댓글

comment-user-thumbnail
2023년 8월 10일

큰 도움이 되었습니다, 감사합니다.

답글 달기