Computer >> 컴퓨터 >  >> 프로그래밍 >> Android

Kotlin으로 Android ListView 안에 포커스 가능한 EditText 구현하기

이 튜토리얼에서는 Kotlin을 사용하여 Android의 ListView 내부에 포커스(focus)가 가능한 EditText를 만드는 방법을 단계별로 알아봅니다. ListView는 기본적으로 자식 뷰의 포커스를 가로채기 때문에, EditText처럼 사용자 입력이 필요한 뷰를 항목 안에 넣으려면 몇 가지 설정이 반드시 필요합니다.

핵심 개념

ListView 안에서 EditText가 정상적으로 포커스를 받도록 하려면 다음 두 가지가 중요합니다.

  • XML에서 android:descendantFocusability="beforeDescendants" 속성을 설정하여 ListView가 자식 뷰보다 먼저 포커스를 처리하도록 합니다.
  • Kotlin 코드에서 listView.itemsCanFocus = true로 설정하여 리스트 항목 내부의 뷰가 포커스를 가질 수 있게 합니다.

1단계: 새 프로젝트 생성

Android Studio에서 File → New Project를 선택하고, 필요한 정보를 모두 입력하여 새 프로젝트를 생성합니다.

2단계: activity_main.xml 작성

res/layout/activity_main.xml 파일에 아래 코드를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="4dp"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:descendantFocusability="beforeDescendants" />

</LinearLayout>

3단계: MainActivity.kt 작성

src/MainActivity.kt에 아래 코드를 추가합니다. 여기서는 BaseAdapter를 상속한 커스텀 어댑터를 통해 각 리스트 항목에 EditText를 배치하고, 포커스가 해제될 때 입력된 값을 데이터 목록에 저장하도록 구현했습니다.

import androidx.appcompat.app.AppCompatActivity
import android.annotation.SuppressLint
import android.app.LauncherActivity
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.EditText
import android.widget.ListView
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    private lateinit var listView: ListView
    private lateinit var myAdapter: MyAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "KotlinApp"
        listView = findViewById(R.id.listView)
        listView.itemsCanFocus = true   // 항목 내부 뷰가 포커스를 가질 수 있도록 설정
        myAdapter = MyAdapter()
        listView.adapter = myAdapter
    }

    private inner class MyAdapter internal constructor() : BaseAdapter() {

        private var layoutInflater: LayoutInflater =
            getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

        internal var myItems = ArrayList<LauncherActivity.ListItem>()
        lateinit var context: Context

        init {
            for (i in 0..6) {
                val listItem = LauncherActivity.ListItem()
                listItem.className = "Caption$i"
                myItems.add(listItem)
            }
            notifyDataSetChanged()
        }

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

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

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

        @SuppressLint("InflateParams")
        override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
            var convertView: View? = convertView
            val holder: ViewHolder

            if (convertView == null) {
                holder = ViewHolder()
                convertView = layoutInflater.inflate(R.layout.list, null)
                holder.caption = convertView.findViewById(R.id.ItemCaption)
                convertView.setTag(holder)
            } else {
                holder = convertView.tag as ViewHolder
            }

            holder.caption.setText(myItems.get(position).className)
            holder.caption.id = position

            // 포커스가 사라질 때 입력값을 데이터에 저장
            holder.caption.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
                if (!hasFocus) {
                    val position = v.id
                    val caption = v as EditText
                    myItems[position].className = caption.text.toString()
                }
            }
            return convertView
        }
    }

    internal inner class ViewHolder {
        lateinit var caption: EditText
    }
}

4단계: list.xml 레이아웃 작성

리스트 항목에 사용될 레이아웃 리소스 파일(res/layout/list.xml)을 생성하고 아래 코드를 추가합니다.

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

    <EditText
        android:id="@+id/ItemCaption"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dip"
        android:singleLine="true" />

</LinearLayout>

5단계: AndroidManifest.xml 확인

androidManifest.xml에 아래와 같이 MainActivity가 등록되어 있는지 확인합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.example.q11">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

실행 및 결과 확인

이제 애플리케이션을 실행해 보겠습니다. 실제 Android 기기를 컴퓨터에 연결했다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바의 Run(실행) 아이콘을 클릭하세요. 실행 옵션에서 연결된 모바일 기기를 선택하면, 기기 화면에 포커스 가능한 EditText가 포함된 ListView가 표시됩니다.

각 항목의 EditText를 탭하면 키보드가 올라오며 텍스트를 수정할 수 있고, 다른 곳을 눌러 포커스가 해제되면 입력한 값이 어댑터의 데이터 목록에 저장됩니다.