ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

Android WidthやHeightとWeightの関係

androidでレイアウトを作成する時、WidthやHeight、Weightの設定でデザインが大きく変わります。

WidthプロパティとHeightプロパティに指定できる値は以下の3種類あります。
match_parent・・・親のViewいっぱいに表示
wrap_content・・・自動調整で表示
fill_parent・・・Android SDK 2.2以降は非推奨。match_parentを使用する。

次にWeightですが「画面上のオブジェクトの占有比率」を指定します。

つまりボタンを3つ配置し、Weight=1、Weight=2、Weight=3と指定すると
Weight=3を指定したボタンが一番大きく、次にWeight=2、Weight=1の順でボタンが大きく表示されるということです。
しかし、WidthやHeightの設定によって、まったく逆の結果になってしまうこともあります。
Weight=1を指定したボタンが一番大きく、次にWeight=2、Weight=3の順でボタンが大きく表示されることもあります。

具体的に見ていきます。
以下はLinearLayout(Vertical)にボタンを3つ配置して、ボタンのプロパティをそれぞれ設定します。

まずボタンのHeightにwrap_contentを指定した場合です。

1つ目のボタン
Width=wrap_content
Height=wrap_content
weight=1

2つ目のボタン
Width=wrap_content
Height=wrap_content
weight=2

3つ目のボタン
Width=wrap_content
Height=wrap_content
weight=3
次はボタンのHeightにmatch_parentを指定した場合です。

1つ目のボタン
Width=wrap_content
Height=match_parent
weight=1

2つ目のボタン
Width=wrap_content
Height=match_parent
weight=2

3つ目のボタン
Width=wrap_content
Height=match_parent
weight=3

Heightプロパティにwrap_contentを指定した場合は
Weight=3を指定したボタンが一番大きく、次にWeight=2、Weight=1の順でボタンが大きく表示されます。

Heightプロパティにmatch_parentを指定した場合は
Weight=1を指定したボタンが一番大きく、次にWeight=2、Weight=3の順でボタンが大きく表示されます。

原因はコチラに詳しく解説されています。
zaki日記 LinearLayout の width(またはheight) と weight の関係
つまりweightは余白を配分する割合を指定するもので、
wrap_contentを指定し場合は余白が正の値になるため、weightに指定した値で余白を分配できるが
match_parentを指定した場合は余白が負の値になるため、weightに指定した値で分配するとおかしなことになるとのことです。
なるほど~

次は横パターンで見ていきます。
LinearLayout(Horizontal)にボタンを2つ配置して、Width=wrap_contentを指定しweightは指定していない状態です。

1つ目のボタン
Width=wrap_content
Height=wrap_content


2つ目のボタン
Width=wrap_content
Height=wrap_content

それぞれのボタンにweight=1を指定します。

1つ目のボタン
Width=wrap_content
Height=wrap_content
weight=1


2つ目のボタン
Width=wrap_content
Height=wrap_content
weight=1

右のボタンの方が大きいですね。
wrap_contentはボタンに設定されているテキストからボタンサイズを計算し、さらにweightの指定による余白の分配をしているので当然の結果ですね。

次はそれぞれのボタンにWidth=match_parent、weight=1を指定した状態です。

1つ目のボタン
Width=match_parent
Height=wrap_content
weight=1


2つ目のボタン
Width=match_parent
Height=wrap_content
weight=1


左右のボタンの大きさが同じになりました。
しかし、2つのボタンのweightが共に1なので問題ありませんが、それぞれweightを変えてみると、縦配置と同じような現象が発生します。
1つ目のボタンをweight=1、2つ目のボタンをweight=2とすると、2つ目のボタンが小さくなってしまいました。

1つ目のボタン
Width=match_parent
Height=wrap_content
weight=1


2つ目のボタン
Width=match_parent
Height=wrap_content
weight=2


widthやheightにwrap_contentを指定した上でweightを指定するということは、ウィジェットに設定されている内容からウィジェットのサイズを自動計算し、 さらにweightの指定による余白の分配が行われます。
そのため少しパフォーマンスが悪くなるようです。

weightに指定した比率でボタンを配置したい場合は、縦配置であればHeight=0dip、横配置であればWidth=0dipに指定し、Weightを指定するようにします。

縦配置の例です。

1つ目のボタン
Width=wrap_content
Height=0dip
weight=1

2つ目のボタン
Width=wrap_content
Height=0dip
weight=2

3つ目のボタン
Width=wrap_content
Height=0dip
weight=3
横配置の例です。

1つ目のボタン
Width=0dip
Height=wrap_content
weight=1

2つ目のボタン
Width=0dip
Height=wrap_content
weight=2

Android パッケージ名

androidプロジェクトをを作成する際、アプリケーション名を入力すると、 パッケージ名は「com.example.xxxxx」となります。
Android Marketに登録する時、「com.example.xxxxx」のままだとエラーで登録できません。

通常、パッケージ名にはドメイン名を逆に並べたものを指定することが推奨されています。
たとえば会社ドメインが「mycompany.co.jp」であれば、パッケージ名は「jp.co.mycompany」となります。
ドメイン名は一意なので、これをパッケージ名に使うことでパッケージ名も一意が保障されることになります。

ドメインを持っていない個人の場合は、
日本インターネット協会 Java 部会のパッケージ名登録/利用サービスというサービスがあります。
http://www.java-conf.gr.jp/Package/naming/index.html
こちらのサービスを利用すると、一意なパッケージ名を取得できます。
取得したパッケージ名は「jp.gr.java_conf.xxxxx」となります。

ドメイン名をお持ちでない方はコチラの利用をお勧めします。

Androi 最新の開発環境 2014年9月現在

2014年9月現在のAndroidの最新の開発環境の作成手順です。
説明はすべてwindowsの64ビットマシンを前提にしています。

Javaのインストール


以下のサイトからJDKをダウンロードします。

http://www.oracle.com/technetwork/java/javase/downloads/index.html

ダウンロードした「jdk-8u20-windows-x64.exe」を実行し、JDKのインストールを行います。

インストールが終了したら、環境変数を設定します。
コンピュータのプロパティ画面より「システムの詳細設定」リンクをクリックし「システムプロパティ画面」を開きます。

「詳細設定」タブの「環境変数」ボタンをクリックします。


「JAVA_HOME」の値を、先ほどJDKをインストールしたパスに変更します。
JDKのデフォルトのインストールパスは「C:\Program Files\Java\jdk1.8.0_20」になっています。

「システム環境変数」のリストより「Path」を選択し、「%JAVA_HOME%bin」が含まれていることを確認します。
Pathに含まれていない場合は、編数値の末尾に「;」をつけ、「%Java_HOME%bin;」を追記します。


Eclipseのインストール


以下のサイトからEclipse4.4をダウンロードします。
http://www.eclipse.org/downloads/
ダウンロードしたファイル「eclipse-standard-luna-R-win32-x86_64.zip」を解凍します。
解凍してできた「eclipse」フォルダを任意の場所に配置します。
私はC:\Program Files\eclipse4.4\に配置しました。

Eclipseの日本語化


「Pleiades」というサードパーティ製のソフトウェアを使用します。 以下のページよりダウンロードします。
http://mergedoc.sourceforge.jp/
少し下にスクロールして「安定板」を選択します。
「pleiades_1.5.0.zip 」をタウンロードします。
ダウンロードしたファイル「pleiades_1.5.0.zip」を解凍します。
解凍してできた「pleiades_1.5.0」フォルダの中身をすべて、「C:\Program Files\eclipse4.4\」フォルダの中に上書きします。

次にeclipse.ini を編集します。
C:\Program Files\eclipse4.4\eclipse.iniをテキストエディタで開きます。
最後に「-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar」1行を追記します。
-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20140603-1326
-product
org.eclipse.epp.package.standard.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.7
-Xms40m
-Xmx512m
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar  

eclipseのクリーン起動を行います。
C:\Program Files\eclipse4.4\eclipse.exe -clean.cmdをダブルクリックして実行します。
eclipse が起動し日本語化されていることを確認します。
これは日本語化に伴う作業なので、1回だけ実行します。
通常Eclipse を起動するときは、 eclipse.exe をダブルクリックして実行します。


Android SDKのインストール


Android SDKはAndroidアプリを開発するために必要なライブラリや、AndroidアプリをPC上で動作させるためのAndroid端末エミュレータなどの開発ツールがまとまった開発キットです。
以下のページよりダウンロードします。

http://developer.android.com/intl/ja/sdk/index.html
少し下にスクロールして「VIEW ALL DOWNLOADS AND SIZES」を選択し、「SDK Tools Only」の「installer_r23.0.2-windows.exe 」をダウンロードします。
ダウンロードしたインストーラーを実行し、SDKをインストールします。

Android Devlopment Tools(ADT)のインストール

Android Devlopment Tools(ADT)をインストールします。
ADTはExlipseにAndroidアプリの開発機能を追加するためのプラグインです。
Eclipseからインストールします。
Eclipseを起動し、 「ヘルプ」メニューにある「新規ソフトウェアのインストール…」 を選択します。
開いたダイアログの「追加」ボタンを押します。

「リポジトリーの追加」ダイアログに以下を入力します。
名前:任意
ロケーション:ADTプラグインの配布サイトのURL 「https://dl-ssl.google.com/android/eclipse/」
表示されているもの全部にチェックを入れて、「次へ」ボタンをクリックします。
インストールの途中で警告が出ますが、無視してOKをクリックします。 インストールが完了したらEclipseを再起動します。

以上で、環境の作成は終了です。

Android CheckBoxの画像を変更する(BackGround) その2

前回「Android CheckBoxの画像を変更する その1」でコチラ「Android GridView CheckBox付きの画像を表示」で作成したチェックボックスの画像を変更してみました。
CheckBoxを画像の右上に配置したいのですが、これ以上動きません。

実はCheckBox画像の右横にもう一つ画像があるのです。
CheckBoxのbackgroundに設定されている@android:drawable/btn_check_label_background は透明の画像で
チェック画像とテキストの間隔を調整しているようです。
ということでbackgroundに@nullを設定してみました。
一つ目のCheckBoxはデフォルトの状態です。
二つ目のCheckBoxはtextに値を設定し、backgroundに@nullを設定しています。
三つ目のCheckBoxはtextに値を設定せず、backgroundに@nullを設定していますが、表示されません。
<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" >

    <CheckBox
        android:id="@+id/checkBox1"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox" />
    
    <CheckBox
        android:id="@+id/CheckBox02"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox" 
        android:background="@null" />   

    <CheckBox
        android:id="@+id/CheckBox03"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
         android:text=""
        android:background="@null" />

</LinearLayout>
btn_check_label_backgroundに替わる画像を作ってあげればよさそうですが、
検索すると「Y.A.M の 雑記帳」さんの Android RadioButton の画像とテキストの間隔を広げる でshapeを使った方法が紹介されていました。

バックグランドに設定するShapeを定義する

res/drawableに「custom_checkbox_background.xml」を作成します。
<?xml version="1.0" encoding="utf-8"?>
<shape  xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
     <solid android:color="#A9A9A9" />
     <padding        
         android:left="22dip"        
         android:top="0dip"        
         android:right="0dip"        
         android:bottom="0dip" />
</shape>

styleを定義する

res/values/styles.xmlを編集し、CheckBoxに設定するスタイルを追記します。
<resources>

 <style name="CustomCheckBox" parent="android:Widget.CompoundButton.CheckBox">
  <item name="android:button">@drawable/custom_checkbox</item>
  <item name="android:background">@drawable/custom_checkbox_background</item>
 </style>
    
</resources>

CheckBoxにStyleを設定する

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" >

    <CheckBox
        android:id="@+id/checkBox1"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox" />
    
    <CheckBox
        android:id="@+id/CheckBox02"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />   

</LinearLayout>

実行してみた



GridViewのCheckBoxにも適用してみました。
右上に配置できています。
shapeの角を丸くして背景色をグラデーションにすればいい感じじゃないでしょうか。

Android CheckBoxの画像を変更する その1

ここ「Android Wiki ボタンの画像変更(CheckBox,RadioButton)」を見れば一発だけど、一応メモ。

画像を用意する

まずチェックボックスの4つの状態を表す画像を用意します。
画像はres/drawable-nodpiに配置します。
  • チェックONかつ選択されていない状態の画像 [checkbox_checked]


  • チェックONかつ選択されている状態の画像 [checkbox_checked_selected]


  • チェックOFFかつ選択されていない状態の画像 [checkbox_unchecked]


  • チェックOFFかつ選択されている状態の画像 [checkbox_unchecked_selected]

画像を定義する

res/drawableフォルダに「custom_checkbox.xml」を作成します。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- チェックON かつ 選択されている状態 -->
    <item
        android:state_checked="true"
        android:state_pressed="true"
        android:drawable="@drawable/checkbox_checked_selected" />
    <!-- チェックON かつ 選択されていない状態-->
    <item
        android:state_checked="true"
        android:drawable="@drawable/checkbox_checked" />
    <!-- チェックOFF かつ 選択されている状態-->
    <item
        android:state_checked="false"
        android:state_pressed="true"
        android:drawable="@drawable/checkbox_unchecked_selected" />
    <!-- チェックOFF かつ 選択されていない状態-->
    <item
        android:state_checked="false"
        android:drawable="@drawable/checkbox_unchecked" />
</selector>

styleを定義する

res/values/styles.xmlを編集し、CheckBoxに設定するスタイルを追記します。
<resources>
    <style name="CustomCheckBox" parent="android:Widget.CompoundButton.CheckBox">
        <item name="android:button">@drawable/custom_checkbox</item>
    </style>
</resources>

CheckBoxにStyleを設定する

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" >

    <CheckBox
        android:id="@+id/CheckBox03"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox" />

    <CheckBox
        android:id="@+id/CheckBox02"
        style="@style/CustomCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="true"
        android:text="CheckBox" />

</LinearLayout>

実行してみた

ハイ。ちゃんとできました。

そしてハマった。

で、以前コチラ「Android GridView CheckBox付きの画像を表示」で作成したチェックボックスに適用してみたのですよ。
私はCheckBoxを画像の右上に配置したいのに、これ以上ガンとして動きません。
レイアウトをいじり倒しても動かないし・・・結構ハマりました・・・

実はCheckBox画像の右横にもう一つ画像があるのですよ。
次回はこの画像をどうにかしたいと思います。

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


}

Android ActionBarとFragmentを利用したTab画面でFragment#OnCreateが2回走る

結構ハマった・・・。

以前作ったActionBarを使ったTab画面で
Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)
Android ActionBarとFragmentを使用してTab画面を表示する(Android 4.0以上)
エミュレータの縦横を切り替えると、描画が2重になっているような現象になってました。
FragmentのOnCreateが2回呼ばれていて、Fragmentが2つ重なっているような感じ。

原因はTabLisnerでした。
エミュレータの縦横を切り替えると、Activityが再作成される。
このときFragmentは自動で復元されており1回目のonCreateが走る。
で、Activityが再作成されたので、TabLisnerも再作成されてonTabSelectedが走る。
この時インスタンス変数mFragmentはnullなのでFragmentManagerに再度addされ
2つ目のFragmentがインスタンス化され2回目のonCreateが走る。
たぶん・・・こんな感じ・・・?

なのでコンストラクタでFragmentを探すように変更し
onTabSelectedでもdetachされていないときだけattachするよう変更しました

コードは Android ActionBarとFragmentを使用してTab画面を表示する(Android 2.x)
のTabListenerです。
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 implements ActionBar.TabListener {

    private Fragment mFragment;
    private final SherlockFragmentActivity mActivity;
    private final String mTag;
    private final Class mClass;
    
    /**
     * @brief コンストラクタ
     * @param activity
     * @param tag
     * @param clz
     */
    public TabListener(SherlockFragmentActivity activity, String tag, Class clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        //FragmentManagerからFragmentを探す。
        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するよう変更
            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) {
    }
}

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

}

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