2012/04/30

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

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

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

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

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

リストビューの1つの要素のXMLはこんな感じ(object.xml)
<?xml version="1.0" encoding="utf-8"?>
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/flipper"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <!-- ViewFlipperの初期画面(1ページ目) -->
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <TextView
            android:id="@+id/text_view"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
        
    </LinearLayout>

    <!-- ViewFlipperの2ページ目 -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <Button
            android:id="@+id/button"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
        
    </LinearLayout>
</ViewFlipper>

テスト用のアクティビティのXML(main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        
    </ListView>

</LinearLayout>

そしてサンプルコードはこんな感じ(MainActivity.java)
package com.andcreate.sample.viewflipperlistview;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ViewFlipper;

public class MainActivity extends Activity{
    
    @Override
    public void onCreate(Bundle bundle){
        super.onCreate(bundle);
        setContentView(R.layout.main);
        
        ListView listView = (ListView)findViewById(R.id.listview);
        
        List<ObjectData> objects = new ArrayList<ObjectData>();
        for(int i = 0; i < 50; i++){
            if(i % 5 == 0){
                objects.add(new ObjectData("text:" + i, "button:" + i, false));
            }
            else{
                objects.add(new ObjectData("text:" + i, "button:" + i, true));
            }
        }
        
        listView.setAdapter(new SampleAdapter(this, objects));
    }
    
}

class ObjectData{
    String textStr;
    String buttonStr;
    boolean isBtn; //ボタンを表示するかどうか
    
    public ObjectData(String textStr, String buttonStr, boolean isBtn){
        this.textStr = textStr;
        this.buttonStr = buttonStr;
        this.isBtn = isBtn;
    }
}

class ViewHolder{
    ViewFlipper flipper;
    TextView textView;
    Button button;
}

class SampleAdapter extends ArrayAdapter<ObjectData>{
    private LayoutInflater inflater;
    
    public SampleAdapter(Context context, List<ObjectData> objects){
        super(context, 0, objects);
        this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        ViewHolder holder;
        if(convertView == null){
            convertView = inflater.inflate(R.layout.object, parent, false);
            holder = new ViewHolder();
            holder.flipper = (ViewFlipper)convertView.findViewById(R.id.flipper);
            holder.textView = (TextView)convertView.findViewById(R.id.text_view);
            holder.button = (Button)convertView.findViewById(R.id.button);
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder)convertView.getTag();
        }
        ObjectData data = getItem(position);
        if(data.isBtn){
            //ボタンなら
            holder.button.setText(data.buttonStr);
            holder.flipper.setDisplayedChild(1); //2ページ目を表示
        }else{
            //テキストビューなら
            holder.textView.setText(data.textStr);
            holder.flipper.setDisplayedChild(0); //1ページ目を表示
        }
        return convertView;
    }
}


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