Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)

Android 3.0(API level 11)から導入されたActionBarとFragmentを使用して、Android 2.x系でもタブ画面を表示する方法です。

タブ画面関係の過去記事
Android TabActivityとTabHostを使用してTab画面を表示する
Android ActionBarとFragmentを使用してTab画面を表示する(Android 4.0以上)


対象:
Build SDK:Android 4.0 (API 14) / Min SDK:Android 2.1 (API 7)

Android 2.xでFragmentを使用できるようにする

eclipseメニューの「ウインドウ」→「Android SDK マネージャー」を起動します。
「Extras」の「Android Support Library」をインストールします。

Android 2.xでActionBarを使用できるようにする

Android Android2.xでActionBarを使用する を参考にして、ActionBarSherlockライブラリを参照に追加します。

タブ画面を表示するためのコード

まずメインとなるアクティビティを作成します。
Android 3.0以降はandroid.app.Activityを継承して作成しますが、Android3.0以前はcom.actionbarsherlock.app.SherlockFragmentActivityを継承して作成します。
自動で生成されるonCreateOptionMenuメソッドはSherlockFragmentActivityではfinalメソッドでOverrideできないのでコメントアウトします。
またonCreateメソッドではをsetCpmtemtView setContentViewメソッドより前にテーマを設定する必要があります。
テーマを設定しないと予期せぬエラーで落ちます。
import com.actionbarsherlock.app.SherlockFragmentActivity;

import android.os.Bundle;

public class MainActivity extends SherlockFragmentActivity {   
  
     @Override  
     public void onCreate(Bundle savedInstanceState) {   
          super.onCreate(savedInstanceState);
          //setCpmtemtViewメソッドより前にテーマを設定する。   
          setTheme(R.style.Theme_Sherlock_Light); 
          setContentView(R.layout.activity_main);   
     }   
  
//     @Override  
//     public boolean onCreateOptionsMenu(Menu menu) {   
//          getMenuInflater().inflate(R.menu.activity_main, menu);   
//          return true;   
//     }   
  
}  
MainActivityのレイアウトです。
res/layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

</FrameLayout>

次に各タブのコンテンツとなるFragmentを作成します。
Android 3.0以降は android.app.Fragmentを継承して作成しますが、Android3.0以前はandroid.support.v4.app.Fragmentを継承して作成します。
レイアウトはそれぞれの違いがわかるように適当に変更しておいてください。
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab1Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab1, container, false);
    }
}
今回は面倒なので2タブにします。
同じように2タブめに表示するFragmentを作成します。
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab2Fragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // 第3引数のbooleanは"container"にreturnするViewを追加するかどうか
        //trueにすると最終的なlayoutに再度、同じView groupが表示されてしまうのでfalseでOKらしい
        return inflater.inflate(R.layout.fragment_tab2, container, false);
    }
}

次にタブを変更したときのリスナーを作成します。
com.actionbarsherlock.app.ActionBar.TabListenerを実装します。
Android3.0以前でFragment関係のクラスを使用する場合は、android.support.v4.appパッケージのものを使用します。
Android3.0以前でActionBar関係のクラスを使用する場合は、 com.actionbarsherlock.appパッケージのものを使用します。
このクラスの各メソッドの引数「FragmentTransaction」はバグっており使用できません。
SherlockFragmentActivityからFragmentManagerオブジェクトを取得するメソッドはgetFragmentManagerメソッドではなくgetSupportFragmentManagerメソッドになります。
2012/12/11追記
縦横を切り替えたときFragment#OnCreateが2回走る不具合があり、
コンストラクタでFragmentを探すように変更し
onTabSelectedでもdetachされていないときだけattachするよう変更しました。
詳しくは・・・
Android ActionBarとFragmentを利用したTab画面でFragment#OnCreateが2回走る
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;

public class TabListener<T extends Fragment> implements ActionBar.TabListener {

 private Fragment mFragment;
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class mClass;
    
    /**
     * コンストラクタ
     * @param activity
     * @param tag
     * @param clz
     */
    public TabListener(SherlockFragmentActivity activity, String tag, Class clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        //FragmentManagerからFragmentを探す。  2012/12/11 追記 
    mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag);   
    }

    /**
     * @brief  タブが選択されたときの処理
     */
    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        //ftはnullではないが使用するとNullPointExceptionで落ちる
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            FragmentManager fm = mActivity.getSupportFragmentManager();
            fm.beginTransaction().add(R.id.container, mFragment, mTag).commit();
        } else {
            //detachされていないときだけattachするよう変更   2012/12/11 変更
            //FragmentManager fm = mActivity.getSupportFragmentManager();
            //fm.beginTransaction().attach(mFragment).commit();
            if (mFragment.isDetached()) {   
                FragmentManager fm = mActivity.getSupportFragmentManager();   
                fm.beginTransaction().attach(mFragment).commit();   
             } 
        }
    }
    /**
     * @brief  タブの選択が解除されたときの処理
     */
    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        //ftはnullではないが使用するとNullPointExceptionで落ちる
        if (mFragment != null) {
            FragmentManager fm = mActivity.getSupportFragmentManager();
            fm.beginTransaction().detach(mFragment).commit();
       }    
    }
    /**
     * @brief タブが2度目以降に選択されたときの処理
     */
    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

それではメインアクティビティに戻ります。
メインアクティビティではonCreateメソッドでActionBarを取得し、NavigationModeをタブにします。
ActionBarを取得するメソッドはgetActionBarメソッドではなくgetSupportActionBarメソッドになります。
後はActionBarにタブのコンテンツとなるFragmentを追加していくだけです。
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;

import android.os.Bundle;

public class MainActivity extends SherlockFragmentActivity {   
   
    @Override  
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        //setCpmtemtViewメソッドより前にテーマを設定する。   
        setTheme(R.style.Theme_Sherlock_Light);   
        setContentView(R.layout.activity_main);   
        
        // Set up the action bar.
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        actionBar.addTab(actionBar.newTab() 
                   .setText("ページ1") 
                   .setTabListener(new TabListener<Tab1Fragment>( 
                              this, "tag1", Tab1Fragment.class))); 
        actionBar.addTab(actionBar.newTab() 
                   .setText("ページ2") 
                   .setTabListener(new TabListener<Tab2Fragment>( 
                              this, "tag2", Tab2Fragment.class))); 

    }   
  
//    SherlockActivityではOverrideできない。   
//    @Override   
//    public boolean onCreateOptionsMenu(Menu menu) {   
//        getMenuInflater().inflate(R.menu.activity_main, menu);   
//        return true;   
//    }   
}  

Android 2.1 (API 7) のエミュレータで実行した結果です。

Android Android2.xでActionBarを使用する

Android 3.0(API level 11)から導入されたAction Barを Android 2.x系でも使用できるようにするには、 ActionBarSherlockライブラリを使用します。

導入方法はコチラが参考になります。
Android 2.xでAction Barが使えるActionBarSherlockの使い方

使い方はコチラが参考になります。
ActionBarSherlockライブラリを使って、2.xでもActionBarを使用する

ActionBarSherlockライブラリを作成する

ActionBarSherlockを下記サイトよりダウンロードし解凍します。
http://actionbarsherlock.com/

eclipseメニューの「ファイル」→「新規」より「Android」ノードの「既存コードからのAndroidプロジェクト」を選択します。

「RootDirectory」に解凍したActionBarSherlockフォルダ内のlibraryを指定します。
「Projects」に表示されているlibraryをチェックONします。
「プロジェクトをワークスペースにコピー」をチェックONします。

「library」プロジェクトが作成されるので、名前はなんでもいいですが「ActionBarSherlockLib」にリネームします。

Javaコンパイラーのバージョンが1.6以前だとエラーになるので変更します。
「ActionBarSherlockLib」プロジェクトを右クリックし「プロパティー」を選択します。
「Java コンパイラー」ノードを選択し「コンパイラー準拠レベル」を1.6に変更します。


ActionBarSherlockライブラリを参照する

ActionBarSherlockLibライブラリを使用する側のプロジェクトを作成します。
Build SDKはAndroid 4.0 (API 14)、Min SDKをAndroid 2.1 (API 7)にしました。

作成したプロジェクトを右クリックし「Android」ノードを選択します。
ライブラリーに「ActionBarSherlockLib」を追加します。

ActionBarを表示させたいActivityはSherlockActivityを継承するよう変更します。
自動で生成されるonCreateOptionMenuメソッドはSherlockActivityではfinalメソッドなのでOverrideできないのでコメントアウトします。
またonCreateメソッドではをテーマを設定する必要があります。
setContentViewメソッドより前にテーマを設定するsetThemeメソッドを呼び出します。
テーマを設定せずにエミュレータで実行しようとすると、実行時エラーになり実行できません。
import com.actionbarsherlock.app.SherlockActivity;

import android.os.Bundle;

public class MainActivity extends SherlockActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setCpmtemtViewメソッドより前にテーマを設定する。
        setTheme(R.style.Theme_Sherlock);
        setContentView(R.layout.activity_main);
    }

//    SherlockActivityではOverrideできない。
//    @Override
//    public boolean onCreateOptionsMenu(Menu menu) {
//        getMenuInflater().inflate(R.menu.activity_main, menu);
//        return true;
//    }
}

Android 2.1
Activityを継承

Android 2.1
SherlockActivityを継承
テーマ「Theme_Sherlock」
Android 2.1
SherlockActivityを継承
テーマ「Theme_Sherlock_Light」
Android 4.0
Activityを継承

Android 4.0
SherlockActivityを継承

Android データがなければInsert、あればUpdate

データがなければInsert、あればUpdateを行いたい時があると思います。
UPDATE と INSERT をもじって、UPSERT と呼ぶそうです。へぇ~

SELECTの結果が0件ならINSERTそれ以外はUPDATEとしてもよいのかもしれませんが
これを1つのSQLで実行することができます。

Oracleでは 「MERGE」

主キーに該当するデータがあればUPDATE、なければINSERTを行います。

    MERGE INTO 表名1 [エイリアス名]
    USING 表名2 | 副問い合わせ [エイリアス名] 
    ON ( 結合条件 )
    WHEN MATCHED THEN
     UPDATE SET カラム名 = 値, …
    WHEN NOT MATCHED THEN
     INSERT (カラム名, … )
     VALUES ( 値, … );


MySQLでは 「REPLACE」 または 「INSERT ON DUPLICATE KEY UPDAT」

INSERT ON DUPLICATE KEY UPDATでは主キーに該当するデータがあればUPDATE、なければINSERTを行います。
REPLACEでは主キーに該当するデータがあればDELTE後INSERTを行い、なければUPDATEを行います。
ただし、検索条件が主キーではないとき、該当データを削除しUPDATEを行います。

   
    INSERT INTO 表名 (カラム名1, カラム名2, ...)
    VALUES (値1, 値2, ...)
    ON DUPLICATE KEY UPDATE カラム名x=値x,カラム名y=値y, ...;


SQLiteでは 「REPLACE」 または 「INSERT OR REPLACE」

こちらはMySQLと違ってINSERT OR REPLACEの別名がREPLACEなので機能は同じで、
主キーに該当するデータがあればUPDATE、なければINSERTを行います。

   
    REPLACE INTO 表名(カラム名) VALUES (値);

Android GridView CheckBox付きの画像を表示

前回「Android GridViewにSDカードの画像を表示する 」の応用です。

今回はCheckBox付きの画像を表示したいと思います。
「SELECT」ボタンでチェックONの画像数を表示します。


Android 4.0 / API 14

MainActivityのLayoutです。
LinearLayoutにButtonとGridViewを配置します。
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/ButtonSelect"
        android:width="120dip" />

    <GridView
        android:id="@+id/gridView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnWidth="70dp"
        android:gravity="center"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth" >
    </GridView>

</LinearLayout>

GridViewn内の個々のアイテムのレイアウトです。
RelativeLayoutにImageViewとCheckBoxを配置します。
res/layout/grid_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:paddingBottom="10dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:paddingTop="10dp" />

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true" />

</RelativeLayout>

次はチェック状態と画像を管理するクラスです。
画像IDをインスタンス変数で保持しておき、サムネイル画像は必要になった時に取得するようにしています。
CheckedImage.java
import android.content.Context;
import android.graphics.Bitmap;
import android.provider.MediaStore;

public class CheckedImage {
    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;
    }
    
    public CheckedImage(boolean checked, long bitmapId){
        mChecked = checked;
        mBitmapId = bitmapId;
    }
    
}

チェックと画像を表示するためのAdapterクラスです。
ArrayAdapterを継承して作成しています。
getView()メソッドでは一度作成したViewは使い回すようにします。
getCheckedItem()メソッドではチェックされているアイテムを取得します。
CheckBoxのOnCheckedChangeListenerでは、CheckedImageオブジェクトのチェック状態を変更します。
CheckedImageArrayAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
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 ViewHolder {
        int position;
        ImageView imageview = null;
        CheckBox checkbox = null;
    }

    private final static int LAYOUT_ID = R.layout.grid_item;


    private Context mContext;
    private LayoutInflater mInflater;   


    public CheckedImageArrayAdapter(Context context, List<CheckedImage> objects) {
        super(context, LAYOUT_ID, objects);
        mContext = context;
        mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    }

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

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

        return convertView;
    }

    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;
    }

    private OnCheckedChangeListener CheckBox1_OnCheckedChangeListener = new OnCheckedChangeListener(){
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            View view = (View)buttonView.getParent();
            ViewHolder holder = (ViewHolder)view.getTag();
            CheckedImage item = CheckedImageArrayAdapter.this.getItem(holder.position);
            item.setChecked(isChecked);        
        }};
}

MainActivityのコードです。
SDカード内の画像IDを取得し、CheckedImageオブジェクトを作成します。
「Select」ボタンのクリックで、チェックONのアイテム数を表示します。
MainActivity.java
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.GridView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private GridView mGridView = null;
    private Button mButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mGridView = (GridView)findViewById(R.id.gridView1);
        mButton = (Button)findViewById(R.id.button1);
        mButton.setOnClickListener(ButtonSelect_OnClickListener);

        //SDカードより画像データのIDを取得
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //SDカード       
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);        
        ArrayList<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();
        }

        //グリッド用のアダプターを作成
        CheckedImageArrayAdapter adapter = new CheckedImageArrayAdapter(getApplicationContext(),lstItem);
        //グリッドにアダプターをセット
        mGridView.setAdapter(adapter);

    }


    private OnClickListener ButtonSelect_OnClickListener = new OnClickListener(){
        public void onClick(View view) {
            CheckedImageArrayAdapter adapter = (CheckedImageArrayAdapter)mGridView.getAdapter();
            List<CheckedImage> lstCheckedItem = adapter.getCheckedItem();
            Toast.makeText(MainActivity.this, String.valueOf(lstCheckedItem.size()), Toast.LENGTH_LONG).show();
        }
    };

}

.NET ブラウザからXML Webサービスを呼び出す

いつも忘れてしまうのでメモ。

HTTP-GET を使用してブラウザーで Web サービスをテストするには
http://<servername>/webservicename.asmx/Methodname?parameter=value

servername :Web サービスが配置されているサーバー名。

webservicename.asmx :Web サービスの .asmx ファイルの名前。

Methodname :Web サービスが公開するパブリック メソッドの名前。

parameter : メソッドが要求するパラメーター名と値。

Android 警告「Use a layout_height of 0dip instead of wrap_content for better performance」

警告「Use a layout_height of 0dip instead of wrap_content for better performance」が出たら
layout_weightの設定によるコンポーネントのサイズ計算が走った後、無意味なwrap_contentのサイズ計算が走ってしまうので
layout_weightを指定しているときはlayout_widthまたはlayout_height(LinearLayoutのorientationにより異なる)を[wrap_content」ではなく「0dip」に設定すると、パフォーマンスが良くなるよということ。

Android 警告「Missing contentDescription attribute on image」

警告「Missing contentDescription attribute on image」が出たら
contentDescriptionプロパティに画像が表示されなかった時の説明文を設定します。
<ImageView
    android:id="@+id/imageView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@string/imageview_description" />

Android GridViewにSDカードの画像を表示する

前回「Android GridView 基本的な使い方」に続いてGridViewにSDカードの画像を表示します。

  1. GridView内で画像を表示するViewを作成する。
  2. GridViewに画像を設定するためのアダプターを作成する
  3. データとなるSDカード内の画像を配列に取得する
  4. グリッドにアダプターを設定する

Android 4.0 / API 14

まずMainActivityのレイアウトです。
GridViewを配置します。
res/layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <GridView
        android:id="@+id/gridView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:horizontalSpacing="10dp"
        android:numColumns="3"
        android:verticalSpacing="10dp" >
    </GridView>

</FrameLayout>

次にGridView内で画像を表示するためのレイアウトを作成します。
ImageViewを配置します。
res/layout/grid_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_action_search" />

</LinearLayout>

画像を設定するためのアダプターを作成します。
ArrayAdapterを継承して作成します。
getView()メソッドでは先ほど作成したレイアウト「grid_item」よりImageViewを取得し画像を設定しています。
public class BitmapAdapter extends ArrayAdapter<Bitmap> {
 
 //GridView内で画像を表示するために作成したレイアウト
    private static final int RESOURCE_ID = R.layout.grid_item;
    
    private LayoutInflater mInflater;
    
    public BitmapAdapter(Context context, List<Bitmap> objects) {
        super(context, RESOURCE_ID, objects);
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = mInflater.inflate(RESOURCE_ID, null);
        }
        ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView1);
        imageView.setImageBitmap(getItem(position));
        return convertView;
    }
    
}

MainActivityのコードです。
Androidではすべての画像はデータベースで管理されており、以下のコードでCursorオブジェクトを取得します。
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
SDカードではなく本体に保存された画像を取得するには、「INTERNAL_CONTENT_URI」を指定します。
Uri uri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
次にCursorオブジェクトをループし、 MediaStore.Images.Thumbnails.getThumbnail()メソッドでサムネイル画像を取得し配列に格納していきます。
※getThumbnail()メソッドはサムネイル画像がなければ生成して返してくれます。
public class MainActivity extends Activity {

    private GridView mGridView = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mGridView = (GridView)findViewById(R.id.gridView1);
        
        //グリッド4列表示
        mGridView.setNumColumns(4);
        //表示する画像を取得
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; //SDカード       
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        ContentResolver cr = getContentResolver();
        ArrayList<Bitmap> lstBitmap = new ArrayList<Bitmap>();
        cursor.moveToFirst();   
        for (int i = 0; i < cursor.getCount(); i++){
            long id = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
            Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(cr, id, MediaStore.Images.Thumbnails.MICRO_KIND, null);
            lstBitmap.add(bmp);
            cursor.moveToNext();
        }
        //アダプター作成
        BitmapAdapter adapter = new BitmapAdapter(getApplicationContext(), lstBitmap);
        //グリッドにアダプタを設定
        mGridView.setAdapter(adapter);
        
       
    }
}

Android GridView 基本的な使い方

GridViewのもっとも基本的な使い方です。
  1. データとなるString配列を作成する
  2. String配列用のアダプターを作成する
  3. グリッドにアダプターを設定する
public class MainActivity extends Activity {

    private GridView mGridView = null;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mGridView = (GridView)findViewById(R.id.gridView1);
        //グリッド4列表示
        mGridView.setNumColumns(4);
        //表示する文字列データ作成
        List<String> lstStr = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            lstStr.add("item " + String.valueOf(i));
        }
        //グリッド用のアダプターを作成
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                this, android.R.layout.simple_list_item_1, lstStr);
        //グリッドにアダプターをセット
        mGridView.setAdapter(adapter); 
       
    }
}