본문 바로가기
Android Studio

[Android 앱개발 숙련] 어댑터 뷰, 리스트 뷰, 그리드 뷰 (# 추가하기)

by 젼젼39 2024. 4. 9.

1. 어댑터 뷰

    : 여러개의 항목을 다양한 형식(이미지뷰 + 텍스트뷰 등)으로 나열하고 선택할 수 있는 기능을 제공하는 뷰

    // 하나 만들어두고 재사용하는...st

    - 표시할 항목 데이터를 직접 관리하지 않고, 어댑터라는 객체로부터 공급받음

어댑터 뷰 <--> 어댑터 데이터 원본
1) 리스트 뷰     : 항목을 수직으로 나열
2) 그리드 뷰     : 항목을 격자 형태로 나열
    - 객체 배열
- 리소스
- 데이터베이스
- 컨텐츠 프로바이더

 

    1) 어댑터 : 데이터를 관리하며, 데이터 원본과 어댑터뷰 사이의 중계 역할 수행
                    = 어댑터에 정의된 인터페이스를 바탕으로 필요한 정보를 요청해,
                        항목 뷰를 화면에 표시하거나 선택된 항목뷰를 처리함

        (1) 어댑터뷰가 어댑터를 사용하기 위해서
                ㄱ. 데이터 원본이 어댑터에 설정되어야 함
                ㄴ. 어댑터뷰에는 어댑터가 설정되어야 함

               == 어댑터가 원본을 가지고 있어야 하고, 어댑터뷰는 그 어댑터와 연결되어있어야 함

        (2) 어댑터뷰는 항목을 표시하기 위해 먼저 표시할 항목의 총 개수를 알아야 함
                - 어댑터 뷰는 어댑터의 getCount() 메소드를 통해 현재 어댑터가 관리하는 데이터 항목의 총 개수를 반환

        (3) 어댑터 뷰가 어댑터의 getView() 메소드를 통해 화면에 실제로 표시할 항목 뷰를 얻고 화면에 표시

        - 사용자가 어댑터뷰의 특정 위치의 항목을 선택했을 때,
            어댑터뷰는 선택된 항목, 항목의 ID, 항목의 뷰를
                어댑터의 getItem(), getItemId(), getView() 메소드를 통해 얻어와서
            이를 항목선택 이벤트 처리기에 넘김 

 

    2) 어댑터 종류

  (1) BaseAdapter - 어댑터 클래스의 공통 구현
- 사용자 정의 어댑터 구현 시 사용
  (2) ArrayAdapter - 객체 배열이나 리소스에 정의된 배열로부터 데이터를 공급받음
  (3) CursorAdapter - 데이터베이스로부터 데이터를 공급받음
  (4) SimpleAdapter - 데이터를 Map(키, 값)의 리스트로 관리
- 데이터를 XML 파일에 정의된 뷰에 대응시키는 어댑터

 

 

 

2. 리스트 뷰 (ListView)

        : 어댑터 뷰의 대표 위젯. 복수 개의 항목을 수직으로 표시

-  리스트 뷰 설정 절차

(1) 프로젝트 생성 (ex. SimpleListViewTest 프로젝트)

(2) 메인화면 레이아웃에 ListView 위젯 정의(XML 코드)

    - 메인 화면 레이아웃 (ex. activity_main.xml 에 ListView 위젯 추가)

    - xml 레이아웃 파일에 정의된 ListView 위젯을 Java 코드에서 참조하기 위해 id 속성을 정의

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

 

(3) 어댑터 객체 생성 (Kotlin 코드)

    - 데이터 원본이 배열인 경우에 ArrayAdapter 객체 사용

    - ArrayAdapter 생성자 => ArrayAdapter(Context context, int resource, int textViewResourceId, T[ ] objects)

        - context : 현재 콘텍스트

       - resource : 항목으로 표시될 텍스트 뷰의 리소스 ID

  리소스 ID 설명
  android.R.layout.simple_list_item_1 하나의 텍스트 뷰로 구성된 레이아웃
  android.R.layout.simple_list_item_2 두 개의 텍스트 뷰로 구성된 레이아웃
  android.R.layout.simple_list_item_checked 오른쪽에 체크 표시가 나타남
  android.R.layout.simple_list_item_single_choice 오른쪽에 라디오 버튼이 나타남
  android.R.layout.simple_list_item_multiple_choice 오른쪽에 체크 버튼이 나타남

        - objects : 어댑터로 공급될 데이터 원본으로 단순 배열

    //string 배열을 이용한 ArrayAdapter 객체 생성 예제

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        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)

   }
}

 

(4) ListView 객체에 어댑터 연결 (Kotlin 코드) 

    - 현 화면 레이아웃(ex. activity_main.xml)에 정의된 뷰 중에서 id가 listView인 ListView 객체를 ViewBinding 통해 얻어옴

    - 얻어온 ListView 객체에 생성된 어댑터 객체(ex. ArrayAdapter 객체 - adapter)를 연결함

//class MainActivity : AppCompatActivity() {

//    private lateinit var binding: ActivityMainBinding

//    override fun onCreate(savedInstanceState: Bundle?) {
//        super.onCreate(savedInstanceState)

//        binding = ActivityMainBinding.inflate(layoutInflater)
//        setContentView(binding.root)

//        // 데이터 원본 준비
//        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)

        // 어댑터를 ListView 객체에 연결
        binding.listView.adapter = adapter

//    }
//}

 

 

 

3. 그리드 뷰

    : 2차원 스크롤 가능한 그리드에 항목을 표시

1) 그리드 뷰 설정 절차

(1) 새 프로젝트 생성 (ex. SimpleGridViewTest)

(2) 메인화면 레이아웃에 GridView 위젯 정의 (xml 코드)

    - 메인화면 레이아웃(ex. activity_main.xml) 에 GridView 위젯 추가 (추가? 대체? @@@@)

    - xml 레이아웃 파일에 정의된 GridView 위젯을 kotlin 코드에서 참조하기 위해 id속성을 정의

<?xml version="1.0" encoding="utf-8"?>
<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"
    />
  android:columnWidth="100dp" 그리드 항목 하나의 폭을 100dp로 설정
  android:numColumns="auto-fit" 열의 폭과 화면 폭을 바탕으로 자동 계산
  android:verticalSpacing 항목 간의 간격 설정
  android:stretchMode="columnWidth" 열 내부의 여백을 폭에 맞게 채움

 

 

(3) ArrayAdapter 객체를 생성하고 GridView 객체에 연결 (Kotlin 코드)

    1. ArrayAdapter 객체를 생성
    2. id를 바탕으로 메인화면 레이아웃(activity_main.xml)에 정의된 GridView 객체 로딩
    3. 생성된 ArrayAdapter 객체를 GridView 객체에 연결

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 데이터 원본 준비
        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)

        // 어댑터를 GridView 객체에 연결
        binding.gridview.adapter = adapter

    }
}

 

2) 이미지 그리드 뷰

(1) 프로젝트 생성 (ex. ImageGridViewTest)

(2) 메인화면 레이아웃에 GridView 위젯 정의 (xml 코드)

    - 메인화면 레이아웃(activity_main.xml)에 GridView 위젯 추가

    - xml 레이아웃 파일에 정의된 GridView 위젯을 java코드에서 참조하기 위해 id속성을 정의

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <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:gravity="center"
        android:horizontalSpacing="10dp"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth"
        android:verticalSpacing="10dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

(3) 어댑터 정의 (Kotlin 코드)

    - 그리드 뷰의 항목으로 간단한 텍스트가 아닌 이미지를 사용하려는 경우
        => 그리드뷰의 항목으로 이미지를 공급하는 ImageAdapter 를 BaseAdapter로부터 파생해 정의

//MainActivity 에 추가하는듯

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)
            //레이아웃이 200*200짜리를 만들고
            imageView.layoutParams = AbsListView.LayoutParams(200, 200)
            //
            imageView.scaleType = ImageView.ScaleType.CENTER_CROP
            //
            imageView.setPadding(8, 8, 8, 8)
        } else {
            imageView = convertView as ImageView
        }

		//getView를 할 때, 몇 번째 포지션인지. 그 포지션에 해당하는 사진을 여기 넣겠다는 뜻
        imageView.setImageResource(mThumbIds.get(position))
        //set한 뷰 자체를 리턴
        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
    )
}

        //ImageAdapter가 관리하는 데이터는 편의상 ImageAdapter 내부에 Image 리소스 ID 의 배열로 설정(편의상)

      //BaseAdapter의 getCount(), getItem(), getItemId(), getView() 메소드를 재정의
        - getCount 항목의 총 개수를 반환하기 위해 mThumbIds 배열의 크기를 반환
        - getItem()  특정 위치의 항목 반환 위해 mThumbIds 배열의 지정된 위치의 항목을 반환
        - getItemId() 특정 위치의 항목 아이디를 반환 (여기서는 배열의 위치(순서)를 항목의 아이디로 간주함)
        - getView() 첫 번째 파라미터로 주어진 위치의 항목 뷰를 반환
= mThumbIds 배열의 position 위치에 있는 이미지 리소스를 ImageView 의 이미지로 설정하고,
       이 설정된 ImageView 객체를 그리드 뷰의 항목뷰로 반환     
- getView() 메소드의 두번째 파라미터인 convertView는 이전에 생성된 항목뷰(여기선 ImageView)를 의미
     1) 해당 위치의 항목뷰가 처음 만들어지는 경우 -> 새 이미지뷰 객체 만들고 크기, 스케일타입, 패딩 설정
     2) 이전에 이미 만들어진 경우 -> 이를 재사용
- 이미지뷰의 scaleType은 원본 이미지를 이미지 뷰에 맞게 확대 및 축소시킬 때, 어떻게 처리할지를 지정
     - CENTER_CROP : 종횡비를 유지하여 스케일링하며 뷰의 크기 이상으로 채우게 됨을 의미
                                    = 이미지 일부가 잘릴 수 있음 

  

(4) 어댑터를 생성하고 GridView 객체에 연결 (Kotlin 코드) + 항목 클릭 이벤트 처리

    - ImageAdapter 객체를 생성하고 이를 GridView 객체에 연결하기

    - GridView 객체에 연결한 ImageAdapter 객체가 클릭될 때 호출될 내용 추가

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // ImageAdapter 객체를 생성하고 GridView 객체에 연결
        binding.gridview.adapter = ImageAdapter()

        // 항목 클릭 이벤트 처리
        binding.gridview.setOnItemClickListener{ parent, view, position, id ->
            Toast.makeText(this@MainActivity,"" + (position + 1) + "번째 선택",           
                Toast.LENGTH_SHORT).show()
        }
    }
}

    // onItemClickListener의 파라미터들
        - parent : 클릭 이벤트가 발생된 AdapterView
        - view : 실제 클릭 된 AdapterView 안의 View
        - position : 어댑터 내에서 클릭 된 항목/뷰의 위치
        - id : 클릭 된 항목의 id

    //adapterview들이 onclicklistener를 다 갖고 있음... 클릭되면 콜백값으로 parent랑 view, position, id가 넘어오게 됨.
        (그 중에서 position만 필요하면 그것만 밑에서 사용해도 됨.)