2012/04/30

ViewFlipperを使ってリストビューのレイアウトを切り替える

Androidのアプリでよく使われるListViewについて。

すべての要素が同じなら普通にListAdapterをセットすればいいんだけど、間になにか要素を挟みたいときとかなかなか苦労する。ListViewの要素を再利用するためにViewHolderとか使ってるとレイアウトが固定されてしまうので。

今回はViewHolderを使いつつある箇所でListViewの要素のレイアウトを切り替えることができたのでメモ。

ビューの切り替え自体はViewFlipperを使う。

リストビューの1つの要素のXMLはこんな感じ(object.xml)
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/flipper"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent" >  
  6.   
  7.     <!-- ViewFlipperの初期画面(1ページ目) -->  
  8.     <LinearLayout   
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent" >  
  11.   
  12.         <TextView  
  13.             android:id="@+id/text_view"  
  14.             android:layout_width="fill_parent"  
  15.             android:layout_height="fill_parent" />  
  16.           
  17.     </LinearLayout>  
  18.   
  19.     <!-- ViewFlipperの2ページ目 -->  
  20.     <LinearLayout  
  21.         android:layout_width="fill_parent"  
  22.         android:layout_height="fill_parent" >  
  23.   
  24.         <Button  
  25.             android:id="@+id/button"  
  26.             android:layout_width="fill_parent"  
  27.             android:layout_height="fill_parent" />  
  28.           
  29.     </LinearLayout>  
  30. </ViewFlipper>  

テスト用のアクティビティのXML(main.xml)
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ListView  
  8.         android:id="@+id/listview"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent" >  
  11.           
  12.     </ListView>  
  13.   
  14. </LinearLayout>  

そしてサンプルコードはこんな感じ(MainActivity.java)
  1. package com.andcreate.sample.viewflipperlistview;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.app.Activity;  
  7. import android.content.Context;  
  8. import android.os.Bundle;  
  9. import android.view.LayoutInflater;  
  10. import android.view.View;  
  11. import android.view.ViewGroup;  
  12. import android.widget.ArrayAdapter;  
  13. import android.widget.Button;  
  14. import android.widget.ListView;  
  15. import android.widget.TextView;  
  16. import android.widget.ViewFlipper;  
  17.   
  18. public class MainActivity extends Activity{  
  19.       
  20.     @Override  
  21.     public void onCreate(Bundle bundle){  
  22.         super.onCreate(bundle);  
  23.         setContentView(R.layout.main);  
  24.           
  25.         ListView listView = (ListView)findViewById(R.id.listview);  
  26.           
  27.         List<ObjectData> objects = new ArrayList<ObjectData>();  
  28.         for(int i = 0; i < 50; i++){  
  29.             if(i % 5 == 0){  
  30.                 objects.add(new ObjectData("text:" + i, "button:" + i, false));  
  31.             }  
  32.             else{  
  33.                 objects.add(new ObjectData("text:" + i, "button:" + i, true));  
  34.             }  
  35.         }  
  36.           
  37.         listView.setAdapter(new SampleAdapter(this, objects));  
  38.     }  
  39.       
  40. }  
  41.   
  42. class ObjectData{  
  43.     String textStr;  
  44.     String buttonStr;  
  45.     boolean isBtn; //ボタンを表示するかどうか  
  46.       
  47.     public ObjectData(String textStr, String buttonStr, boolean isBtn){  
  48.         this.textStr = textStr;  
  49.         this.buttonStr = buttonStr;  
  50.         this.isBtn = isBtn;  
  51.     }  
  52. }  
  53.   
  54. class ViewHolder{  
  55.     ViewFlipper flipper;  
  56.     TextView textView;  
  57.     Button button;  
  58. }  
  59.   
  60. class SampleAdapter extends ArrayAdapter<ObjectData>{  
  61.     private LayoutInflater inflater;  
  62.       
  63.     public SampleAdapter(Context context, List<ObjectData> objects){  
  64.         super(context, 0, objects);  
  65.         this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  66.     }  
  67.       
  68.     @Override  
  69.     public View getView(int position, View convertView, ViewGroup parent){  
  70.         ViewHolder holder;  
  71.         if(convertView == null){  
  72.             convertView = inflater.inflate(R.layout.object, parent, false);  
  73.             holder = new ViewHolder();  
  74.             holder.flipper = (ViewFlipper)convertView.findViewById(R.id.flipper);  
  75.             holder.textView = (TextView)convertView.findViewById(R.id.text_view);  
  76.             holder.button = (Button)convertView.findViewById(R.id.button);  
  77.             convertView.setTag(holder);  
  78.         }else{  
  79.             holder = (ViewHolder)convertView.getTag();  
  80.         }  
  81.         ObjectData data = getItem(position);  
  82.         if(data.isBtn){  
  83.             //ボタンなら  
  84.             holder.button.setText(data.buttonStr);  
  85.             holder.flipper.setDisplayedChild(1); //2ページ目を表示  
  86.         }else{  
  87.             //テキストビューなら  
  88.             holder.textView.setText(data.textStr);  
  89.             holder.flipper.setDisplayedChild(0); //1ページ目を表示  
  90.         }  
  91.         return convertView;  
  92.     }  
  93. }  

ViewHolderの切り替え自体はViewHolder#showNext()やViewHolder#showPrevious()でやることが多いけど、今回の場合それでやると表示がうまくいかなかったのでViewHolder#setDisplayChild()を使った。
今回の場合、ViewFlipperなので、動的に要素の高さを変更することはできない。できたらなかなかすごいけど何かいい方法があるのかな?