ListViewの項目の背景色を動的に変更したいんだけど…
ListViewにおけるタップ時の色効果変更は…
main.java
ListView list = (ListView)this.findViewById(R.id.listView); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.custom_row, R.id.row_text); list.setAdapter(adapter); list.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, int id){ } }
と、カスタマイズした(といっても今回はシンプルな)レイアウトをセットし、
custom_row.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="match_parent" android:orientation="vertical" android:background="@color/row_color" > <TextView android:id="@+id/row_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFF" /> </LinearLayout>
その対象となるレイアウト内のbackgroundにselectorによる色選択用xmlを指定。
row_color.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:state_pressed="false" android:drawable="@color/black" /> <item android:state_selected="true" android:drawable="@color/red" /> <item android:state_selected="false" android:state_pressed="true" android:drawable="@color/black" /> </selector>
その色選択用xmlをres内に作ったcolorディレクトリに作成。
value内にcolors.xmlを作成して動作に対するオリジナル色の指定もできるようにした。
これで、ListView内のアイテムをタップすると指定した色に変更などができるわけだけど…
今回はListViewの項目をタップしたとき、その背景色を変えたい。
また、同じ項目をもう一度タップしたら背景色を元に、
違う項目をタップしたら先ほどの項目の背景色を元に戻した上でタップした項目の背景色を変更したい。
ようは、シンプルにON/OFFのある単項目の選択状態を明確に作りたい。
タップしている間の色変更はpressed指定で簡単にできるけど、今回はタップ判定ではなく選択判定。
ただ、背景色変更と上記設定の同項目再タップ時の非選択状態への両立が上手くいかない…
選択している以上、selected指定でいけるだろうとonItemClick内でsetSelected()のtrueやfalseを指定しても、
一回trueになるとなにを押してもtrueのままだったり、一回だけtrueであとはfalseだったり…
(setSelected()を使わずにsetBackgroundResource()などで無理やり再現はできるけど、
色指定用にXMLを無駄に作ったりでスマートじゃないし、position指定によるviewの再利用でスクロールに問題があるし、
そもそもselectorのxmlの意味がない…)
失敗パターンのひとつ.java
ListView list = (ListView)this.findViewById(R.id.listView); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.custom_row, R.id.row_text); list.setAdapter(adapter); list.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, int id){ System.out.println(view.isSelected() +":clicked "+ view); if(view.isSelected() == true){ view.setSelected(false); 処理 System.out.println(view.isSelected() +":true_root "+ view); }else if(view.isSelected() == false){ view.setSelected(true); 処理 System.out.println(view.isSelected() +":false_root "+ view); } } }
同じ項目を連打した動きはこんな感じ
わかり辛いけど タップ後の選択状態 : 出力位置 対象view
false:clicked android.widget.LinearLayout@405a9ec0 true:false_root android.widget.LinearLayout@405a9ec0 false:clicked android.widget.LinearLayout@405a9ec0 true:false_root android.widget.LinearLayout@405a9ec0 false:clicked android.widget.LinearLayout@405a9ec0 true:false_root android.widget.LinearLayout@405a9ec0 false:clicked android.widget.LinearLayout@405a9ec0 true:false_root android.widget.LinearLayout@405a9ec0
押したタイミングでselectedがtrueになっているのに同じ項目をもう一度タップするとfalse_rootにはいっている
viewにsetSelectedしても次にタップしたタイミングまで保持されてないみたい…?
スクロールしなくてもItemClickしたらgetViewで新しく生成されなおしてるの?
いや、view自体は同一みたいだし…再利用だからだろうか?
もしそうだとしても今回のレベルでgetView()内を弄るのもぐちゃぐちゃするし…
そこらへん確認したい。
と、書いてて思いついたのが正常に動作した。
修正案.java
ListView list = (ListView)this.findViewById(R.id.listView); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.custom_row, R.id.row_text); list.setAdapter(adapter); list.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, int id){ if(view.equals(clicked) == true){ view.setSelected(false); clicked = null; 処理 }else if(view.equals(clicked) == false){ view.setSelected(true); clicked = view; 処理 } } }
selectedが保持できないならフラグとして使わずにselectorでの判定のみにして、同一viewかをフラグにしてしまえばいい。
無駄に悩ませられたわりにあっさりしたコードになって拍子抜け…
固執しすぎは視野を狭めるいい教訓になりました…