Android
 Computer >> コンピューター >  >> プログラミング >> Android

KotlinでAndroidのListView内にフォーカス可能なEditTextを作成する方法

はじめに

本記事では、Kotlinを使用してAndroidアプリのListView内に、フォーカス(入力)可能なEditTextを作成する方法を解説します。

ListViewの中にEditTextのような入力系Viewを配置すると、リストのスクロール操作とテキスト編集が競合してしまうことがあります。これを解決する鍵となるのが、ListViewのandroid:descendantFocusability属性と、Kotlinコード側のitemsCanFocus設定です。

手順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>

ここでのポイントは、ListViewに指定したandroid:descendantFocusability="beforeDescendants"です。この設定により、ListView自身が先にフォーカスを制御しつつ、子要素であるEditTextがフォーカスを受け取れるようになります。

手順3:MainActivity.ktを実装する

src/MainActivity.ktに以下のコードを記述します。

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
    }
}

このコードでは、BaseAdapterを継承したカスタムアダプター「MyAdapter」を定義しています。listView.itemsCanFocus = trueを設定することで、リスト項目内のEditTextがフォーカスを取得できるようになります。

また、ViewHolderパターンによってViewを再利用し、スクロール時のパフォーマンスを確保しています。さらに、EditTextのフォーカスが外れたタイミングでOnFocusChangeListenerが呼ばれ、その時点の入力内容をデータリスト(myItems)に反映する仕組みです。これにより、リストをスクロールしてViewが再生成されても、編集済みの値が失われることはありません。

手順4:リスト項目用レイアウト(list.xml)を作成する

レイアウトリソースファイル「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に以下のコードを記述します。

<?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」アイコンKotlinでAndroidのListView内にフォーカス可能なEditTextを作成する方法をクリックしてください。候補として表示されたモバイルデバイスを選択すると、実機の画面にアプリが起動し、EditTextを含むリストが表示されます。

KotlinでAndroidのListView内にフォーカス可能なEditTextを作成する方法

まとめ

ListView内にEditTextなどの入力可能なViewを配置する場合は、XML側でdescendantFocusability="beforeDescendants"を、Kotlin側でitemsCanFocus = trueを設定するのがポイントです。さらに、フォーカスが外れたタイミングで入力値をデータに保存することで、View再利用による入力内容の消失を防ぐことができます。リスト形式の入力フォームなどを作成する際に、ぜひ活用してみてください。

  1. Kotlinを使ってAndroidのTextView内に複数のスタイルを作成する方法

    このチュートリアルでは、Kotlinを使用してAndroidアプリのTextView内に太字・斜体・小さめフォント・文字色など、複数のスタイルを同時に適用する方法を解説します。 TextViewでHTMLタグによるリッチテキスト表現を実現するには、Html.fromHtml()メソッドを利用します。このメソッドは新しいAPIでは非推奨(Deprecated)となっているため、サンプルコードでは@Suppress("DEPRECATION")アノテーションを付けて警告を抑制しています。 ステップ1:新規プロジェクトの作成 Android Studioを起動し、メニューから

  2. KotlinでAndroidにスワイプリフレッシュレイアウト(SwipeRefreshLayout)を実装する方法

    この記事では、Kotlinを使用してAndroidアプリにスワイプリフレッシュレイアウト(SwipeRefreshLayout)を実装する方法を、サンプルコードとともにステップごとに解説します。 SwipeRefreshLayoutは、画面を下方向にスワイプするだけでデータを再読み込みできる「Pull to Refresh(引っ張って更新)」操作を簡単に実装できる公式ウィジェットです。ニュースアプリやSNSなどで頻繁に使われるUIパターンなので、覚えておくと非常に便利です。 ステップ1:Android Studioで新規プロジェクトを作成 Android Studioを起動し、メニューから「F