戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
10

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ListViewに編集可能なViewを追加する

カスタマイズした ListView に編集可能なView (EditText や CheckBox 等) を追加する場合には、変更した値をどこかに保持する処理が必要になります。
これは以前、ListView をカスタマイズするで書いたように ListView の項目は再利用されているため値を毎回詰め替える必要があるためです。
さて、変更した値を保持する処理ですが、アダプタクラス内の getView() にて項目毎にイベントを設定することで対応します。

今回の記事では CheckBox を例としてサンプルソースを載せました。

参考サイト:mumoshu.log - [android]ListView + CheckBoxでチェック可能なリストを作るときの注意点

注意しないといけないのはイベントを設定するタイミング。
2回目以降(convertView != null)は項目が使いまわされているため、CheckBoxに設定されているイベントは再利用前の行のイベントが設定されています。

なお、変更した値を外(Activity)で取得するには、CustomCheckAdapter#getItem(int postion) を使います。

----------------------------------------------------------------------
リストの項目のレイアウト
res/layout/sample_row_check.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
android:layout_weight="1"
android:textSize="24sp" />
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp" />
</LinearLayout>

----------------------------------------------------------------------
項目のデータクラス
外部から情報を取得できるようにpublic変数にしています
getter/setter 作るのが面倒だったので直接取得するようにしている
CustomCheckData.java
package jp.inujirushi.sample.adapter;

/**
* アダプタに設定するデータ
*/
public class CustomCheckData {
/** 文字列 */
public String text;
/** チェック状態 */
public boolean isChecked;

/**
* コンストラクタ
*
* @param text
* 文字列
* @param isChecked
* チェック状態
*/
public CustomCheckData(String text, boolean isChecked) {
this.text = text;
this.isChecked = isChecked;
}
}

----------------------------------------------------------------------
アダプタクラス
CustomCheckAdapter.java
package jp.inujirushi.sample.adapter;

import java.util.List;

import jp.inujirushi.sample.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;

public class CustomCheckAdapter extends ArrayAdapter {

LayoutInflater mInflater;

/**
* コンストラクタ
*
* @param context
* コンテキスト
* @param objects
* 行データ
*/
public CustomCheckAdapter(Context context, List objects) {
// 親のコンストラクタを呼び出す
super(context, 0, objects);

// インフレーターを取得する
this.mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ホルダークラスを定義する
ViewHolder holder;

// ビューを設定する
if (convertView == null) {
// ビューに定義したレイアウトをインフレートする
convertView = this.mInflater.inflate(R.layout.sample_row_check,
parent, false);

// ホルダークラスを生成する
holder = new ViewHolder();

// ホルダークラスにレイアウト内のビューを設定する
holder.textView = (TextView) convertView
.findViewById(R.id.textView);
holder.checkBox = (CheckBox) convertView
.findViewById(R.id.checkBox);

// タグにホルダークラスを設定する
convertView.setTag(holder);
} else {
// タグからホルダークラスを取得する
holder = (ViewHolder) convertView.getTag();
}

// 指定された位置のアイテムを取得する
// ※final化してイベント内からも参照できるようにする
final CustomCheckData data = getItem(position);

// ホルダークラスのビューの値を設定する
holder.textView.setText(data.text);

// チェックボックスに変更イベントを設定する
CheckBox chk = holder.checkBox;
chk.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// チェック状態を設定する
data.isChecked = isChecked;
}
});

// イベント設定後にチェックを入れる
// ※イベントより前に設定すると別の行の値が書き換えられてしまう
holder.checkBox.setChecked(data.isChecked);

// 表示するビューを返す
return convertView;
}

/**
* ホルダクラス
*/
class ViewHolder {
/** テキストビュー */
TextView textView;
/** チェックボックス */
CheckBox checkBox;
}
}
※71行目でホルダクラスのチェックボックスを CheckBox クラスに置き換えているのはソース整形時(Ctrl+Shift+F)に崩れてしまうためにやっているだけなので気にしないでね
スポンサーサイト

Comments

イベントより前に設定するという部分について
はじめまして、たじと申します。

「イベントより前に設定する…」
と注意書きしている部分があります。

それがなぜ起こるのか仕組みをご存知でしたらご教授頂けないでしょうか。

現在Androidアプリ開発でカスタムリストを用いてるのですが、イベントハンドリングでつまづいてる状態です。
はじめまして、管理人のとむ・やんくんです(名前変えました)

開設1年、初の書き込みにうれし涙が止まらない状態です。

さて、質問の「イベントより前に設定する・・・」についての回答です。

少し長くなるので、先に一言でまとめると
『イベントが再利用されたViewに関連しているから』です。

以下、説明。

AdapterクラスのgetView()で渡されるViewは使いまわされており、本来想定していた位置の内容が入っているとは限りません。
getItem(position) ≠ convertView(の内容)

そのため、表示を正しくするには値を毎回設定し直す必要があります(68,83行目)

CheckBox#setChecked(boolean) で値を設定した場合
CheckBox には CompoundButton#OnCheckedChangeListener が設定されているため
値が変更されたということで onCheckedChanged() イベントが実行されます。

このイベントは再利用されたViewに紐づいているため
イベントを設定する前に値の変更を行ってしまうと
再利用されているViewの本来の位置の情報が書き換わってしまいます。

例えば…
-----------------------------------------------------
getView() のパラメータで position には 5 が設定されている。
しかし、 convertView には position = 10 の内容が設定されている。

イベント設定前に値を変えてしまった場合
onCheckedChangedイベントで、再利用されたView(position = 10) の内容が書き換えられてしまう。

イベント設定後に値を変えた場合
onCheckedChangedイベントで、再設定した正しいView(position = 5) の内容が書き換えられる。
-----------------------------------------------------
となります。

うーん、記事と似たようなこと書いてる気がしますね…
分からなかったらもう一度聞いてください。
74 onCheckedChanged(  ) でオーバーライドできません
こんにちは。ひでと申します。
「ListView をカスタマイズする」に続けて読ませていただいております。


Eclipse SDK Version: 4.2.1 (日本語化)で
l74: onCheckedChanged(CompoundButton buttonView, boolean isChecked)
において
「型 new CompoundButton.OnCheckedChangeListener(){} の
メソッド onCheckedChanged(CompoundButton, boolean) は
スーパークラスのメソッドをオーバーライドする必要があります」
とのエラーが表示されます。

Eclipseのクイックフィックスに従いl73: @Overrideを除去しても、
エミュレータ上で起動してもアクティビティ表示前にランタイムエラーを起こします。

解決方法を教えていただけないでしょうか。
よろしくお願いします。
スーパークラスのメソッドをオーバーライドする必要があります について
こんばんは。管理人のとむ・やむくんです。
「とむ・やん・くん」と思っていたら「やん」ではなく「やむ」だったため、またもや改名しました。

さて、「スーパークラスのメソッドをオーバーライドする必要があります」ですが、
これは JDKのバージョンが古い時に発生します。
JDKのバージョンを 1.6 以上に上げればエラーは発生しなくなります。

【修正方法】
Eclipseメニューの [ウィンドウ] - [設定] で設定ウィンドウを開く。
Java - コンパイラー の「コンパイラー準拠レベル」を 1.6 以上にする。

※プロジェクト単位で設定する場合(個別に設定していなければ不要)
プロジェクトを右クリックし、プロパティーを選択。
Java コンパイラーの「コンパイラー準拠レベル」を 1.6 以上にする。


以下のサイトが詳しく書いてあるので読んでおくといいかも。

Techbooster - Android開発初心者がよくやる失敗【Javaのバージョンが古い】
http://techbooster.org/android/environment/6626/
ひでです。

早速教えていただきありがとうございます。
「コンパイラー準拠レベル」を 1.6にすることでエラーはなくなりました。
ありがとうございます。

Leave a Comment


Body
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。