2011年07月26日

シンプルな答え(ListView + CheckBox)

この部分のプログラムがうまくいかず、やっている間もう悩むというよりなんだか疲れてしまいました。
今作成中のアプリは完成させるつもりではありますが、あまりにも多くの問題に時間がかかり、完成しても感動できないというか嬉しくもないのではないかという気分です。

ことの起こりは他とほぼ同じ構成なのに一つのクラスだけAsyncTaskを適用したら動かなくなったり、ListViewのチェックボックスがListView.CHOICE_MODE_SINGLEなのに、複数のチェックが入ったりするという奇妙な現象が発生したことでした。
ログを取って確認した結果、以前分からないまま適当な方法でごまかしていたListViewのCheckedなポジションの取得問題(「不思議なListViewの多重呼び出し」(http://shironiji.sblo.jp/article/46736342.html?1311218535))と同じように複数回の呼び出しがされているために起こっているように思えました。そこで、ポジションの取得方法を変えようとしたところ、出口の見えない深い迷路の奥に迷い込むこととなりました。

まず、いままでListViewをカスタマイズしてCheckBoxを組み込むと、CheckBoxは反応しなくなる「のは仕方ない」という情報を信じ込んでいたので、CheckBoxのタップでのみチェックを受け付ける仕様だったのですが、やはりこれは動作として変なので再度調べました。
すると、レイアウトxmlファイルでCheckBoxの方にandroid:focusable="false"及びandroid:focusableInTouchMode="false"を設定してやることで、アイテムの行全体を反応させることができるという情報を改めて見つけて参考にさせていただきました。
Dev.GrAFR様(http://michelle-gf.blogspot.com/2011/02/listviewtextviewcheckbox.html)

しかし、そうするとCheckBoxとアイテム全体が連動しないことになります。(バラバラに動作する)
そこで、共通のメソッドをCheckBoxとアイテムのそれぞれのOnClickListenerで呼び出すようにしました。そして、チェックボックスの操作はようやく見つかった下記サイトを参考にさせていただきました。

MarkMailというサイトのRomain Guyさんのレス(Googleの人?有名な人?)
http://markmail.org/message/gozttt3y5c3akbmc

ただ、ここでは通常のCheckBoxではなく、CheckBoxAdapterなどというものを使っていますし、これで動きそうなところまでいったのですが、今度はListViewに設定してあったスクロールする時に表示されるつまみを出すandroid:fastScrollEnabledが動かなくなりました。
動かないといっても開発用に使っているIDEOSだけ。調子が悪くて現在お休み中の元の開発用実機HT-03Aでは起こらない。他のほぼ同じ構成のクラスでも起こらないという奇妙な現象。
実はこれは最初に表示されるアイテム(表示完了直後に目に見える範囲の行)にチェックを入れると発生することが分かりました。
他のクラスや解像度の違うHT-03Aでは起こらなかったのは、その範囲にチェックを入れていなかったからなのでした。
結局理論的にはどうしてそうなるのか分からないのですが、長い長い、本当に果てしなく長く感じる時間ののち、下記サイトでその解決方法が得られました。

mumoshu.log様のサイト
http://d.hatena.ne.jp/mumoshu/20100606/1275840016

その解決法とは「チェックボックスをsetCheckedするまえにリスナーを登録せよ」ということ。
その理由も理解はできていませんが、とりえずその形になるように設定してみたところ、ようやく期待通りになりました。
そもそもその前までのコードではチェックを入れるまでの過程が非常に複雑で、これではおかしい!もっとシンプルにできるはずだ!と思っていたのですが、これでようやくそのシンプルな答えが得られました。

しかし、今回他の部分も含めて正しい情報に行き当たるのがなかなかできなかったとはいえ、こんなよく使う簡単そうなところで果てしなく時間がかかっている自分って、やっぱりダメだなと思いました。
それから以前に書いたこのブログのエントリーは修正を入れておきますが、やっぱりこのド素人!の書くことを参考にされるのは怖いな、とも思いました。
でも、一応今回のコードの抜粋を入れておきます。これも間違っているかも知れませんが。


※タップしてチェックを入れる時はlistViewのOnClickListener内でListItemAdapter adapter = (ListItemAdapter)listView.getAdapter();で取得したadapterからsetChecked(int position,boolean isChecked)メソッドを呼び出しています。



public class ListItemAdapter extends ArrayAdapter<ListItem> implements CompoundButton.OnCheckedChangeListener{
     private LayoutInflater mInflater;
     private SparseBooleanArray mCheckBoxStatus;

     public ListItemAdapter(Context context,int rid,List<ListItem> custList){
     super(context,rid,custList);
     mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     mCheckBoxStatus = new SparseBooleanArray(custList.size());
     }

     public View getView(int position,View convertView,ViewGroup parent){
     ViewHolder holder;

     if(convertView == null){
     convertView = mInflater.inflate(R.layout.list_item, null);

     //チェックボックスをセット
     CheckBox checkbox = (CheckBox)convertView.findViewById(R.id.check);
            
     //画像をセット
     ImageView image = (ImageView)convertView.findViewById(R.id.image);

     //名前をセット
     TextView name = (TextView)convertView.findViewById(R.id.name);

//プライオリティをセット
     TextView prio = (TextView)convertView.findViewById(R.id.prio);

     holder = new ViewHolder();
     holder.checkbox = checkbox;
     holder.image = image;
     holder.name = name;
     holder.prio = prio;

     convertView.setTag(holder);

     }else{
     holder = (ViewHolder)convertView.getTag();
     }
    
     holder.checkbox.setTag(position);
     holder.checkbox.setChecked(mCheckBoxStatus.get(position, false));
     holder.checkbox.setOnCheckedChangeListener(this);

     holder.checkbox.setChecked(isChecked(position));

     //データを取り出す
     final ListItem item = (ListItem)getItem(position);

     holder.image.setImageDrawable(item.getImage());
     holder.name.setText(item.getName());

     return convertView;
     }

     public boolean isChecked(int position){
     return mCheckBoxStatus.get(position, false);
     }

     public void setChecked(int position,boolean isChecked){
mCheckBoxStatus.put(position, isChecked);
     notifyDataSetChanged();
     }

     public void toggle(int position) {
            setChecked(position, !isChecked(position));
        }

public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
mCheckBoxStatus.put((Integer)buttonView.getTag(),isChecked);
}
    }

posted by 白虹 at 23:47| Comment(0) | TrackBack(1) | Android開発
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/47258688

この記事へのトラックバック

チェックボックスの状態を取得する
Excerpt: チェックボックスの状態を取得する 前回チェックボックスの状態を設定するで設定した チェックボックスの状態を ボタンを押したときに状態を取得したいと思います まず前回のres/layout/main.x..
Weblog: 初心者の初心者のためのAndroidアプリ開発!
Tracked: 2012-01-24 13:13