2012年12月11日火曜日

Android ActionBarとFragmentを利用したTab画面でデータを(GridView)表示する

以前作成したTab画面に、以前作成したSDカード内の画像をチェックボックス付きで表示するGridViewを乗せてみました。
Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)
Android GridView CheckBox付きの画像を表示

Fragment再作成時にデータを保存・復元する


まずグリッドの個々のアイテムであるチェックボックスと画像を管理するクラスCheckedImageに
Parcelableインターフェースを実装しBundleに追加できるようにします。
boolean型をParcelableで保存するには  dest.writeByte((byte) (mBoolean ? 1 : 0));  とします。
boolean型をParcelableで復元するには  mBoolean = (in.readByte() == 1);  とします。
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore;

public class CheckedImage implements Parcelable{
    private boolean mChecked = false;
    private long mBitmapId;
    private Bitmap mBitmap = null;

    public boolean getChecked() {
        return mChecked;
    }
    public void setChecked(boolean checked) {
        this.mChecked = checked;
    }
    public long getBitmapId() {
        return mBitmapId;
    }
    public void setBitmapId(long bitmapId) {
        this.mBitmapId = bitmapId;
    }
    public Bitmap getBitmap(Context context) {
        if (mBitmap == null){
            mBitmap = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(), mBitmapId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
        }
        return mBitmap;
    }
    /**
     * コンストラクタ
     * @param checked
     * @param bitmapId
     */
    public CheckedImage(boolean checked, long bitmapId){
        mChecked = checked;
        mBitmapId = bitmapId;
    }

    /*****************************
     * Parcelable実装のために必要
     *****************************/
    public CheckedImage(Parcel in) {
        // 必ず read と write は同じ順番で入れること。  
        mChecked = (in.readByte() == 1);
        mBitmapId = in.readLong();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 必ず read と write は同じ順番で入れること。  
        dest.writeByte((byte) (mChecked ? 1 : 0));
        dest.writeLong(mBitmapId);    
    }

    @Override
    public int describeContents() {
        // ファイルディスクリプターを使用しないのであれば常に0
        return 0;
    }

    // これは定型文なのでそのまま使える。
    public static final Creator CREATOR = new Creator() {
        public CheckedImage createFromParcel(Parcel in) {
            return new CheckedImage(in);
        }
        public CheckedImage[] newArray(int size) {
            return new CheckedImage[size];
        }
    };


}

次にデータを保存・復元する際に便利なようにArrayAdapterを改良し
adapterに設定されているデータを取得するメソッドgetItemList()を追加します。
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.CompoundButton.OnCheckedChangeListener;


public class CheckedImageArrayAdapter extends ArrayAdapter<CheckedImage> {
    private static class ItemView {
        ImageView imageview = null;
        CheckBox checkbox = null;
    }
    
    private final static int LAYOUT_ID = R.layout.checked_image; 
    
    private Context mContext;
    private LayoutInflater mInflater;   
    private List<CheckedImage> mItemList;

    /**
     * コンストラクタ
     * @param context
     * @param objects
     */
    public CheckedImageArrayAdapter(Context context, List<CheckedImage> itemlist) {
        super(context, LAYOUT_ID, itemlist);
        mContext = context;
        mItemList = itemlist;
        mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    }
    
    /**
     * チェックボックスのチェックONリスナー
     */
    private OnCheckedChangeListener CheckBox1_OnCheckedChangeListener = new OnCheckedChangeListener(){
        public void onCheckedChanged(CompoundButton buttonView,    boolean isChecked) {
            int position =  (Integer)buttonView.getTag();
            CheckedImage item = CheckedImageArrayAdapter.this.getItem(position);
            item.setChecked(isChecked);    
        }};

    /**
     * adapterに設定されているデータを取得する
     * @return
     */
    public ArrayList<CheckedImage> getItemList() {
        return (ArrayList<CheckedImage>) mItemList;
    }
 
   /**
     * adapterに設定されているデータのうちチェックONのデータを取得する
     * @return
     */   
    public List<CheckedImage> getCheckedItem(){
        List<CheckedImage> lstItem = new ArrayList<CheckedImage>();
        for ( int i = 0; i < getCount(); i++) {
            if (getItem(i).getChecked()){
                lstItem.add(getItem(i));
            }
        }
        return lstItem;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemView itemview;
        if (convertView == null) {
            convertView = mInflater.inflate(LAYOUT_ID, null);
            itemview = new ItemView();
            itemview.imageview = (ImageView) convertView.findViewById(R.id.imageView1);
            itemview.checkbox = (CheckBox) convertView.findViewById(R.id.checkBox1);
            itemview.checkbox.setOnCheckedChangeListener(CheckBox1_OnCheckedChangeListener);
            convertView.setTag(itemview);
        } else {
            itemview = (ItemView)convertView.getTag();
        }

        CheckedImage item = getItem(position);
        itemview.checkbox.setTag(position);
        itemview.imageview.setImageBitmap(item.getBitmap(mContext));
        itemview.checkbox.setChecked(item.getChecked());

        return convertView;
    }   
}
次にFragmentです。
縦横切り替えしたときにFragmentの再作成をしたくないので、コンストラクタで  setRetainInstance(true); を設定します。
Fragmentを再作成したときデータを復元できるように、onSaveInstanceStateでadapterに設定してるデータを保存します。
タブページを変更したとき、データを再取得したくないのとチェック状態を保持しておきたいので、onCreateでグリッドに設定するadapterを作成します。 このときonCreateの引数savedInstanceStateに復元データがあれば、そのデータを使用するようにします。
import java.util.ArrayList;
import java.util.List;

import android.content.ContentResolver;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.GridView;



public class MainTab1Fragment extends Fragment {
   
    private static final String CHECKED_IMAGE_LIST = "CheckedImageList";

    private GridView mGridView = null;
    private Button mButtonSelect = null;
    private CheckedImageArrayAdapter mAdapter = null;

    public MainTab1Fragment(){
        setRetainInstance(true);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {    
        super.onCreate(savedInstanceState);

        if (mAdapter == null) {
            if (savedInstanceState != null) {
                @SuppressWarnings("unchecked")
                List<CheckedImage> lstItem = (List<CheckedImage>) savedInstanceState.get(CHECKED_IMAGE_LIST);
                mAdapter = new CheckedImageArrayAdapter(getActivity(), lstItem);
            } else {
                //SDカードより画像データのIDを取得
                ContentResolver cr = getActivity().getContentResolver();
                Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //SDカード       
                Cursor cursor = cr.query(uri, null, null, null, null);        
                List<CheckedImage> lstItem = new ArrayList<CheckedImage>();
                cursor.moveToFirst();   
                for (int i = 0; i < cursor.getCount(); i++){
                    long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
                    lstItem.add(new CheckedImage(false,id));
                    cursor.moveToNext();
                }    
                //グリッド用のアダプターを作成
                mAdapter = new CheckedImageArrayAdapter(getActivity(), lstItem);
            };
        };
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        View view = inflater.inflate(R.layout.fragment_main_tab1, container, false);
        mGridView = (GridView)view.findViewById(R.id.gridView);
        mButtonSelect = (Button)view.findViewById(R.id.select);
        mButtonSelect.setOnClickListener(ButtonSelect_OnClickListener);    
        //グリッドにアダプターをセット
        mGridView.setAdapter(mAdapter);
        return view;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelableArrayList(CHECKED_IMAGE_LIST, mAdapter.getItemList());        
    }


}

0 件のコメント: