2013年10月31日木曜日

Fragmentを使ってみる(Activityとの連携)

今回は、FragmentとActivityのやりとりを試したいと思います(`・ω・´)シャキーン
動作的には、上部のFragmentをクリックすると、下部のFragmentのテキストを変更して、
下部のFragmentをクリックすると、上部のFragmentのテキストを変更するようにしました( ̄ー ̄)ニヤリ

各FragmentのイベントをActivityが受け取り、
そのイベントを別のFragmentに伝えるような流れになっています。
■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment6"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment6.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

■fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eeeeee"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp" />

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

</LinearLayout>
■activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_linearlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/layout_fragment_top"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ff0000"
        android:orientation="vertical" />
    <LinearLayout
        android:id="@+id/layout_fragment_bottom"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ff0000"
        android:orientation="vertical" />
    
</LinearLayout>

■MainFragmentTop.java

FragmentからActivityを取得するには、getActivityを利用します。
//FragmentからActivityの取得する
LinearLayout linearLayout = (LinearLayout) getActivity().findViewById(R.id.layout_linearlayout);
コールバックの設定として、Activityにインターフェイスを実装するやり方をしていますが、
別パターンのリスナーとして登録する方法は避けた方が良いそうです。(´・∀・`)ヘー

こちらのサイトにその内容が書かれていますъ(゚Д゚)グッジョブ!!
Fragment から Activity にコールバックする方法
バックグラウンドにある Fragment はメモリが足りなくなると、
システムによって破棄され、必要になったときにシステムによって再生成されることがあるため、
その際 setOnOkBtnClickListener() 部分は呼ばれないのです。

そのため、システムによって再生成された場合、
コールバックを受け取れなくなるということが起こりえます。

以前のエントリで書きましたが、レイアウトで定義した( で定義した)Fragment は
FragmentTransaction の対象にしてはいけないため、
上記のコードの MainFragment が onCreate() 内で生成されるのではなく、
レイアウトXMLファイルで定義されている場合は、問題になることは(たぶん)あまりないと思います。

ただ、いずれにしてもこの実装はさけた方が懸命です。

で、どうするかというと、Activity 自体にリスナーを実装するようにします。
(私はあまりこの書き方は好きじゃないのですが、Fragment がシステムから再生成されうるのでしょうがないです。。。)

package com.example.fragment6;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainFragmentTop extends Fragment {

 public final static String TAG = "MainFragmentTop";

 // コールバック
 private FragmentTopCallback mCallback;

 /*
  * コールバックのインターフェイス
  */
 public static interface FragmentTopCallback {

  public void onClick(TextView v);

 }

 /*
  * Fragmentが Activityに対して何らかのコールバックを提供する場合、
  * Activityが必要なインタフェースを備えているかどうかチェックなどを行う。
  * 
  * @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
  */
 @Override
 public void onAttach(Activity activity) {
  super.onAttach(activity);
  Log.d(TAG, "FragmentTop-onAttach");

  // Activityがコールバックを実装しているかチェック
  if (activity instanceof FragmentTopCallback == false) {
   throw new ClassCastException(
     "activity が FragmentTopCallback を実装していません.");
  }
  //
  mCallback = (FragmentTopCallback) activity;
 }

 /*
  * Fragment がユーザに見える状態になるまで保持しておくべきコンポーネントの初期化。
  * 
  * @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.d(TAG, "FragmentTop-onCreate");

  // fragment再生成抑止。Fragment を破棄させないようにする。
  setRetainInstance(true);

 }

 /*
  * Fragment が持つ View を構築する状態です。 View を持たない Fragment は、ここで null を返す
  * 
  * @see
  * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
  * android.view.ViewGroup, android.os.Bundle)
  */

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.d(TAG, "FragmentTop-onCreateView");
  // Bundleで保存されたデータを復元
  String title = getArguments().getString("title");

  //
  Log.d("onCreateView", title);

  // FragmentからActivityの取得する
  LinearLayout linearLayout = (LinearLayout) getActivity().findViewById(
    R.id.layout_linearlayout);
  //
  Log.d(TAG, "onCreateView" + String.valueOf(linearLayout));

  // 第1引数:レイアウトXMLファイルのリソースID、
  // 第2引数:
  // 第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
  // trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
  View view = inflater.inflate(R.layout.fragment_main, container, false);
  // FragmentのTextViewの取得
  final TextView text = (TextView) view.findViewById(R.id.text_view);
  //
  text.setText(title);
  //
  text.setTag(TAG);
  // FragmentのImageViewの取得
  ImageView image = (ImageView) view.findViewById(R.id.image_view);
  //
  // image.setTag(TAG);
  //
  image.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if (mCallback != null) {
     mCallback.onClick(text);
    }
   }
  });
  //
  return view;
 }
}
■MainFragmentBottom.java
基本的なコードはMainFragmentTopと殆ど同じコードになります。
package com.example.fragment6;

import android.app.Activity;
import android.os.Bundle;
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.ImageView;
import android.widget.TextView;

public class MainFragmentBottom extends Fragment {

 //
 public static String TAG = "MainFragmentBottom";

 // コールバック
 private FragmentBottomCallback mCallback;

 /*
  * コールバックのインターフェイス
  */
 public static interface FragmentBottomCallback {

  public void onClick(TextView v);

 }

 /*
  * Fragmentが Activityに対して何らかのコールバックを提供する場合、
  * Activityが必要なインタフェースを備えているかどうかチェックなどを行う。
  * 
  * @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
  */
 @Override
 public void onAttach(Activity activity) {
  super.onAttach(activity);
  Log.d(TAG, "FragmentBottom-onAttach");

  // Activityがコールバックを実装しているかチェック
  if (activity instanceof FragmentBottomCallback == false) {
   throw new ClassCastException(
     "activity が FragmentBottomCallback を実装していません.");
  }

  //
  mCallback = (FragmentBottomCallback) activity;
 }

 /*
  * Fragment がユーザに見える状態になるまで保持しておくべきコンポーネントの初期化。
  * 
  * @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.d(TAG, "FragmentBottom-onCreate");

  // fragment再生成抑止。Fragment を破棄させないようにする。
  setRetainInstance(true);

 }

 /*
  * Fragment が持つ View を構築する状態です。 View を持たない Fragment は、ここで null を返す
  * 
  * @see
  * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
  * android.view.ViewGroup, android.os.Bundle)
  */

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.d(TAG, "FragmentBottom-onCreateView");
  // Bundleで保存されたデータを復元
  String title = getArguments().getString("title");

  //
  Log.d("onCreateView", title);

  // 第1引数:レイアウトXMLファイルのリソースID、
  // 第2引数:
  // 第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
  // trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
  View view = inflater.inflate(R.layout.fragment_main, container, false);
  // FragmentのTextViewの取得
  final TextView text = (TextView) view.findViewById(R.id.text_view);
  // テキストを挿入
  text.setText(title);
  // タグを設定する
  text.setTag(TAG);
  // FragmentのImageViewの取得
  ImageView image = (ImageView) view.findViewById(R.id.image_view);
  // 画像クリック時のイベントリスナー
  image.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if (mCallback != null) {
     mCallback.onClick(text);
    }
   }
  });
  //
  return view;
 }

}
■MainActivity.java
各画面にFragmentが複数ある場合のやりとりとして、

Fragmentが2つあり、その中に各クリックイベント用のコールバックが用意されているので、
Activity側でそのインターフェイスを実装する必要があります。

実装用のメソッドが同名で、且つ引数がまったく同じインターフェイスになので、
同じコールバックのonClickが呼びだされてしまいます。
そのため、どっちのFragmentからクリックされたかを内部で判定する必要があります。
判定にはFragment側でTextViewに設定したタグを使って行っています。
package com.example.fragment6;

import com.example.fragment6.MainFragmentBottom.FragmentBottomCallback;
import com.example.fragment6.MainFragmentTop.FragmentTopCallback;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements
  FragmentTopCallback, FragmentBottomCallback {

 public final static String TAG = "MainActivity";

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

  // Fragmentを管理するFragmentManagerを取得
  FragmentManager manager = getSupportFragmentManager();
  // 追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
  FragmentTransaction tx = manager.beginTransaction();

  // 既にFragmentが作成されているかチェック
  if (manager.findFragmentByTag("layout_fragment_top") == null) {

   // 1つ目のfragmentを生成
   MainFragmentTop fragment_top = new MainFragmentTop();
   Bundle bundle1 = new Bundle();
   bundle1.putString("title", "キタ――(゚∀゚)――!!");
   // フラグメントに渡す値をセット
   fragment_top.setArguments(bundle1);

   // Fragment をスタックに追加する
   // メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
   // add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
   // Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
   tx.add(R.id.layout_fragment_top, fragment_top,
     "layout_fragment_top");
  }

  if (manager.findFragmentByTag("layout_fragment_bottom") == null) {
   // 2つ目のfragmentを生成
   MainFragmentBottom fragment_bottom = new MainFragmentBottom();
   Bundle bundle2 = new Bundle();
   bundle2.putString("title", "クル━━━━(゚∀゚)━━━━??");
   // フラグメントに渡す値をセット
   fragment_bottom.setArguments(bundle2);

   // Fragment をスタックに追加する
   // メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
   // add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
   // Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
   tx.add(R.id.layout_fragment_bottom, fragment_bottom,
     "layout_fragment_bottom");
  }

  tx.commit();

 }

 /*
  * 上下に配置されているFragmentの中にあるImageViewのクリックイベントのコールバック
  * 
  * @see com.example.fragment6.MainFragmentTop.FragmentTopCallback#onClick()
  */
 public void onClick(TextView v) {

  Log.d(TAG,
    "onClick TextView " + String.valueOf(v.getTag()) + " : "
      + v.getText());

  // Fragmentを管理するFragmentManagerを取得
  FragmentManager manager = getSupportFragmentManager();

  // TextViewに設定されているタグを元にどっちがクリックされたか判定する
  String tag = (String) v.getTag();
  if (tag == MainFragmentTop.TAG) {
   //
   Log.d(TAG, "onClick " + String.valueOf(v.getTag()));
   // MainFragmentTopがクリックされたら、MainFragmentBottomを取得してテキストを変更する
   MainFragmentBottom mfg = (MainFragmentBottom) manager
     .findFragmentByTag("layout_fragment_bottom");
   if (mfg != null) {
    mfg.setText("(´・ω・`)ショボーン");
   }
  } else if (tag == MainFragmentBottom.TAG) {
   //
   Log.d(TAG, "onClick " + String.valueOf(v.getTag()));
   // MainFragmentBottomがクリックされたら、MainFragmentTopを取得してテキストを変更する
   MainFragmentTop mfg = (MainFragmentTop) manager
     .findFragmentByTag("layout_fragment_top");
   if (mfg != null) {
    mfg.setText("(`・ω・´)シャキーン");
   }
  }

 }
}
実行結果画面は次のような感じになります!


以上です(`・ω・´)ゞビシッ!!

参考URL

コールバックの実装(Activityにインターフェイスを実装させる)

前回の「コールバックの実装(リスナーとして登録) 」の続きです!(`・ω・´)シャキーン
今回は、Activityにインターフェイスを実装させるパターンを行いたいと思います。

その前にインターフェイス作成時の注意点(゚∀゚)キタコレ!!

こちらの記事から抜粋メソッド本体の定義
インタフェースを実装したクラスではインタフェース内で定義されたメソッドの本体を定義する必要があります。
この本体の定義がメソッドの具体的な振る舞いとなります。クラス内でメソッド本体の定義を行う場合いくつかの注意点があります。

・インタフェース内で定義されたメソッドは暗黙的にpublic、abstract修飾子が付与されています。
そのため、クラス内でメソッドの定義を行う場合は明示的にpublic修飾子を付与する必要があります。

・インタフェースを実装したクラスではそのインタフェースで宣言されたすべてのメソッドの本体を定義する必要があります。
すべてを定義しない場合はそのクラスはabstractクラスと宣言される必要があります。

(´・∀・`)ヘー

テストしたコードは次のようになります。
■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.callback2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.callback2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

■activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000">
    <GridView android:id="@+id/grid_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:numColumns="4"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp">
    </GridView>
</LinearLayout>

■CustomAdapter.java
Adapterの内容は前回と同じになります。
package com.example.callback2;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class CustomAdapter extends ArrayAdapter<String> {

 //
 private Context context = null;
 // アダプターに渡したリスト
 private ArrayList<String> data = null;
 //
 private int resource = 0;

 // コールバック用のリスナー
 private OnButtonClickListener mListener;

 /*
  * コールバックの定義
  */
 public interface OnButtonClickListener {

  // クリック処理
  public void onClick(int position);

 }

 /*
  * コールバックの設定
  */
 public void setOnButtonClickListener(OnButtonClickListener listener) {
  mListener = listener;
 }

 public CustomAdapter(Context context, int resource, ArrayList<String> data,
   OnButtonClickListener listener) {
  super(context, resource, data);
  //
  this.context = context;
  this.resource = resource;
  this.data = data;
  this.mListener = listener;
 }

 /*
  * 指定された項目を識別するためのIDを取得する
  */
 @Override
 public long getItemId(int position) {
  return position;
 }

 /*
  * 指定された項目を取得する
  */
 @Override
 public String getItem(int position) {
  return data.get(position);
 }

 /*
  * データの項目数を取得する
  */
 @Override
 public int getCount() {
  return data.size();
 }

 /*
  * 1アイテム分のビューを取得.
  * 
  * @param Integer アイテムのポジション番号
  * 
  * @param View 可能な場合不要なビューを再利用する。膨大な量の情報を出力する場合にオブジェクトを可能な限り再利用する。
  * nullの場合には再利用できるオブジェクトがないためオブジェクトを生成する。
  * 
  * @param ViewGroup 選択されたAdapterのビューとなる。GridViewになる。
  */
 @Override
 public View getView(final int position, View convertView, ViewGroup parent) {

  // Viewが無かった場合
  if (convertView == null) {
   // コンストラクタで渡されたレイアウトのリソースIDからViewを取得するため、LayoutInflaterを取得する
   LayoutInflater inflater = (LayoutInflater) getContext()
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   // レイアウトファイルをViewに変換する(レイアウトに記述されているルートのViewになる)
   convertView = inflater.inflate(resource, null);
  }
  // 取得したビューのキャスト
  TextView viewText = (TextView) convertView;

  final View v = convertView;
  //
  viewText.setText(String.valueOf(position));
  //
  viewText.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    Log.d("CoustomAdapter", "onClick" + String.valueOf(position));
    if (mListener != null) {
     mListener.onClick(position);
    }
   }

  });

  return viewText;
 }

}
■MainActivity.java

Activityにインターフェイスを実装するために、
CustomAdapterで記述したインターフェイスのCustomAdapter.OnButtonClickListenerを
implements(インプリメント)する必要があります。

次に、インターフェイスを実装させる為に、
ActivitiyにonClickを定義します。
package com.example.callback2;

import java.util.ArrayList;

import com.example.callback2.CustomAdapter.OnButtonClickListener;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.widget.GridView;

public class MainActivity extends Activity implements OnButtonClickListener {

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

  // GridViewに表示するデータを作成する
  ArrayList<String> list = new ArrayList<String>();

  int length = 100;
  for (int i = 0; i < length; i++) {
   list.add(String.valueOf(i));
  }

  // ArrayAdapterを生成する
  CustomAdapter adapter = new CustomAdapter(getApplicationContext(),
    android.R.layout.simple_list_item_1, list);

  // 自分自身をリスナーとして渡す
  adapter.setOnButtonClickListener(this);

  // GridViewを取得する
  GridView gridView = (GridView) findViewById(R.id.grid_view);

  // GridViewにカスタムアダプターを設定する
  gridView.setAdapter(adapter);
 }

 /*
  * 
  */
 public void onClick(int position) {
  Log.d("OnButtonClickListener", "onClick" + String.valueOf(position));
 }

}
この記述で、ActivityにonClick用のコールバックが実装できます( ´∀`)bグッ!

インターフェイスは複数実装できますので、
その際に2つのインターフェイスに実装しなければいけないメソッド名が同じだった場合には、
Activityに実装時に処理が分かれます。

・メソッド名が同じで、引数が違う場合には、Activityで別々に実装してあげる。
(個別のコールバックとして呼び出される)

・メソッド名と引数が同じ場合には、Activityで1つだけ実装してあげる。
(共通のコールバックとして呼び出される)

引数までが同じであれば同じものが呼び出されます。

同名だけど、引数が違う場合には個別のものが呼び出されます(゚д゚)(。_。)(゚д゚)(。_。) ウンウン
以上です(`・ω・´)ゞビシッ!!

参考URL

2013年10月30日水曜日

コールバックの実装(リスナーとして登録)

コールバックの実装方法に某J氏が悩んでたので説明代わりに記述を( ´_ゝ`)σ)Д`)ツンツン

例として、単純なGridViewのリストをクリックした際に、
Activityでそのクリックイベントをコールバックする仕組みにしました(`・ω・´)シャキーン

コールバックの設定方法として次の2パターンがあります。
1)リスナーとして設定するバターン
2)Activityにインターフェイスを実装するパターン

個人的には、1のリスナーとして設定する方法が好きです(´∀`*)ポッ
なので、まずはリスナーとして登録するパターンを試したいと思います(ΦωΦ)フフフ…

■CustomAdapter.java
GridViewに渡すAdapaterのソースになります。

コールバック用にOnButtonClickListener用の変数 、OnButtonClickListenerメソッド 、
setOnButtonClickListenerメソッドの3つが定義されています。
(それ以外はGridViewを表示させるためのものになります。)

・OnButtonClickListener用の変数
設定されたリスナーを保持するための変数。

・OnButtonClickListenerメソッド(インターフェイス)
コールバック用のインターフェイスを定義します。
コールバックの受け取り側(Activity)で処理の実装を行います。
実装させたいメソッドをここで定義しておきます。例ではonClickとして定義しています。
任意の名前とコールバックを受け取りたい側へ渡す引数を設定する事が出来ますが、
実装側で記述する際には合わせる必要があります。

・setOnButtonClickListenerメソッド
コールバックを受け取りたい側からインターフェースを実装したクラスのインスタンスをセットする

実際のソースコードは次のようになっています。(`・ω・´)シャキーン
package com.example.callback1;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class CustomAdapter extends ArrayAdapter<String> {

 //
 private Context context = null;
 // アダプターに渡したリスト
 private ArrayList<String> data = null;
 //
 private int resource = 0;

 // コールバック用のリスナー
 private OnButtonClickListener mListener;

 /*
  * コールバックの定義
  */
 public interface OnButtonClickListener {

  // クリック処理
  public void onClick(int position);

 }

 /*
  * コールバックの設定
  */
 public void setOnButtonClickListener(OnButtonClickListener listener) {
  mListener = listener;
 }

 public CustomAdapter(Context context, int resource, ArrayList<String> data) {
  super(context, resource, data);
  //
  this.context = context;
  this.resource = resource;
  this.data = data;
 }

 /*
  * 指定された項目を識別するためのIDを取得する
  */
 @Override
 public long getItemId(int position) {
  return position;
 }

 /*
  * 指定された項目を取得する
  */
 @Override
 public String getItem(int position) {
  return data.get(position);
 }

 /*
  * データの項目数を取得する
  */
 @Override
 public int getCount() {
  return data.size();
 }

 /*
  * 1アイテム分のビューを取得.
  * 
  * @param Integer アイテムのポジション番号
  * 
  * @param View 可能な場合不要なビューを再利用する。膨大な量の情報を出力する場合にオブジェクトを可能な限り再利用する。
  * nullの場合には再利用できるオブジェクトがないためオブジェクトを生成する。
  * 
  * @param ViewGroup 選択されたAdapterのビューとなる。GridViewになる。
  */
 @Override
 public View getView(final int position, View convertView, ViewGroup parent) {

  // Viewが無かった場合
  if (convertView == null) {
   // コンストラクタで渡されたレイアウトのリソースIDからViewを取得するため、LayoutInflaterを取得する
   LayoutInflater inflater = (LayoutInflater) getContext()
     .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   // レイアウトファイルをViewに変換する(レイアウトに記述されているルートのViewになる)
   convertView = inflater.inflate(resource, null);
  }
  // 取得したビューのキャスト
  TextView viewText = (TextView) convertView;

  //
  viewText.setText(String.valueOf(position));
  //
  viewText.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    Log.d("CoustomAdapter", "onClick" + String.valueOf(position));
    if (mListener != null) {
     mListener.onClick(position);
    }
   }

  });

  return viewText;
 }

}

■MainActivity.java(匿名クラスを利用してリスナー登録)
package com.example.callback1;

import java.util.ArrayList;
import com.example.callback1.CustomAdapter.OnButtonClickListener;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.GridView;

public class MainActivity extends Activity {

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

  // GridViewに表示するデータを作成する
  ArrayList<String> list = new ArrayList<String>();

  int length = 100;
  for (int i = 0; i < length; i++) {
   list.add(String.valueOf(i));
  }

  // ArrayAdapterを生成する
  CustomAdapter adapter = new CustomAdapter(getApplicationContext(),
    android.R.layout.simple_list_item_1, list);

  // 匿名クラスでリスナー登録
  adapter.setOnButtonClickListener(new OnButtonClickListener() {

   public void onClick(int position) {
    Log.d("MainActivity", "onClick" + String.valueOf(position));
   }
  });

  // GridViewにカスタムアダプターを設定する
  gridView.setAdapter(adapter);
 }

}
匿名クラスの場合には、次のコードだけでリスナーとしてコールバックを登録できます。
OnButtonClickListenerは、先ほど定義したCustomAdapterクラスのOnButtonClickListenerを指しています。 (上部でインポートしているため。)
  // 匿名クラスでリスナー登録
  adapter.setOnButtonClickListener(new OnButtonClickListener() {
 
   public void onClick(int position) {
    Log.d("MainActivity", "onClick" + String.valueOf(position));
   }
  });
インターフェイス名が被っている場合には、クラス名まで記述する事で回避する事が可能です。
  // 匿名クラスでリスナー登録
  adapter.setOnButtonClickListener(new CustomAdapter.OnButtonClickListener() {
 
   public void onClick(int position) {
    Log.d("MainActivity", "onClick" + String.valueOf(position));
   }
  });
MainActivityは次のように記述しても同じように動作します。
変更点は、リスナーの登録時に匿名クラスを利用せずに、
内部クラスの定義を行って登録を行っています。

■MainActivity.java(匿名クラスを利用せずに内部クラスを作成してリスナー登録)
package com.example.callback1;

import java.util.ArrayList;
import com.example.callback1.CustomAdapter.OnButtonClickListener;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.GridView;

public class MainActivity extends Activity {

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

  // GridViewに表示するデータを作成する
  ArrayList<String> list = new ArrayList<String>();

  int length = 100;
  for (int i = 0; i < length; i++) {
   list.add(String.valueOf(i));
  }

  // ArrayAdapterを生成する
  CustomAdapter adapter = new CustomAdapter(getApplicationContext(),
    android.R.layout.simple_list_item_1, list);

  // 内部クラスをインスタンス化して登録
  adapter.setOnButtonClickListener(new ButtonClickListener());
  // GridViewを取得する
  GridView gridView = (GridView) findViewById(R.id.grid_view);

  // GridViewにカスタムアダプターを設定する
  gridView.setAdapter(adapter);
 }

 /*
  * リスナーを内部クラスとして定義する
  */
 public class ButtonClickListener implements OnButtonClickListener {

  public void onClick(int position) {
   Log.d("ButtonClickListener", "onClick" + String.valueOf(position));
  }

 }

}
簡単な説明をしますと、次の箇所でリスナー用の内部クラスを定義しています。
 /*
  * リスナーを内部クラスとして定義する
  */
 public class ButtonClickListener implements OnButtonClickListener {

  public void onClick(int position) {
   Log.d("ButtonClickListener", "onClick" + String.valueOf(position));
  }

 }
定義した内部クラスをインスタンス化して、それをリスナーとして登録しています。
  // 内部クラスをインスタンス化して登録
  adapter.setOnButtonClickListener(new ButtonClickListener());
こっちの方が登録の部分はスッキリしてますね(゚д゚)(。_。)(゚д゚)(。_。) ウンウン

匿名クラスと内部クラスのどっちを使って記述するようにしてるかというと、
個人的には匿名クラス内に記述する内容が多くなる場合には、
内部クラスを利用して記述するようにしています。

もしくはその時の気分次第⊂二二二( ^ω^)二⊃ブーン

内部クラスのが見やすい気がして見た目はこっちのが好きだったりしますけど、記述が多くなる・・・(´;ω;`)ウッ…

以上です(`・ω・´)ゞビシッ!!

参考URL

2013年10月29日火曜日

Fragmentを使ってみる(画面回転時のライフサイクルについて)

今回は、Fragment利用時の画面回転のライフサイクルについてになります。
前回の「Fragmentを使ってみる(ライフサイクルについて)」の続きな感じです(ΦωΦ)フフフ…

画面の回転が発生するとActivityで次の流れで実行されActivityが再構築されます。
1.onPause
2.onStop
3.onDestroy
4.onCreate
5.onStart
6.onResume
FragmentはActivityと同様のライフサイクルを持っているため、
上記の流れと同じイベントが発生されます。

そのため、画面が回転されるたびにFragmentのonCreateViewも毎回呼ばれてしまいます。

その状態でonCreateでFragmentの初期化処理として、
ネット上からデータを持ってきたりとか行うような処理があった場合に、
画面が回転されるたびに、毎回実行されてしまい非効率になっていします(´;ω;`)ブワッ

それを回避する為に用意されているのが、setRetainInstanceになるそうです。

setRetainInstanceを設定するとどうなるかというと、次のサイトを参考に抜粋しましたъ(゚Д゚)グッジョブ!!
Activity再生成時のデータの保存・復元(Fragment#setRetainInstance)
Fragment#setRetainInstance(true) を呼び出すことでどうなるかというと、

Activity再生成時に Fragment#onDestroy、onCreate が呼ばれなくなる。
Activity再生成時には Fragment#onDetach、onAttach が呼び出されるだけ。
Fragmentインスタンスが破棄されないので Fragmentインスタンス内にデータをそのまま保持しておくことが可能。

とのことです。φ(゚Д゚ )フムフム…

早速、使って見て動作を確認したい思います(`・ω・´)シャキーン

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment5"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment5.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eeeeee"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

■activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/layout_fragment_1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ff0000"
        android:orientation="vertical" />

</LinearLayout>

■MainFragment.java
package com.example.fragment5;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainFragment extends Fragment {
 public static String TAG = "MainFragment";

 /*
  * Fragmentが Activityに対して何らかのコールバックを提供する場合、
  * Activityが必要なインタフェースを備えているかどうかチェックなどを行う。
  * 
  * @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
  */
 @Override
 public void onAttach(Activity act) {
  super.onAttach(act);
  Log.d(TAG, "Fragment-onAttach");
 }

 /*
  * Fragment がユーザに見える状態になるまで保持しておくべきコンポーネントの初期化。
  * 
  * @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.d(TAG, "Fragment-onCreate");

  // fragment再生成抑止。Fragment を破棄させないようにする。
  setRetainInstance(true);

 }

 /*
  * Fragment が持つ View を構築する状態です。 View を持たない Fragment は、ここで null を返す
  * 
  * @see
  * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
  * android.view.ViewGroup, android.os.Bundle)
  */

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.d(TAG, "Fragment-onCreateView");
  // Bundleで保存されたデータを復元
  String title = getArguments().getString("title");

  //
  Log.d("onCreateView", title);

  // 第1引数:レイアウトXMLファイルのリソースID、
  // 第2引数:
  // 第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
  // trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
  View view = inflater.inflate(R.layout.fragment_main, container, false);
  //
  TextView text = (TextView) view.findViewById(R.id.text_view);
  text.setText(title);
  //
  return view;
 }

 /*
  * Activity の onCreate の状態の処理が終わったことを示す状態です。
  * 
  * @see android.support.v4.app.Fragment#onActivityCreated(android.os.Bundle)
  */
 @Override
 public void onActivityCreated(Bundle bundle) {
  super.onActivityCreated(bundle);
  Log.d(TAG, "Fragment-onActivityCreated");
 }

 /*
  * Fragment の UI が構築され、ユーザに見える状態です。
  * 
  * @see android.support.v4.app.Fragment#onStart()
  */
 @Override
 public void onStart() {
  super.onStart();
  Log.d(TAG, "Fragment-onStart");
 }

 /*
  * Fragment の UI が構築され、ユーザとのインタラクションが出来るようになった状態です。
  * 
  * @see android.support.v4.app.Fragment#onResume()
  */
 @Override
 public void onResume() {
  super.onResume();
  Log.d(TAG, "Fragment-onResume");
 }

 /*
  * ユーザが別の画面への遷移をしようとして Fragment から離れていこうとした状態です。 Activity
  * と同じく、この時点で、永続化するべき情報を保存するようにしておきます。 必ずしもこの後の状態へ遷移し、Fragment
  * がメモリから破棄されるわけではありません。
  * 
  * @see android.support.v4.app.Fragment#onPause()
  */
 @Override
 public void onPause() {
  super.onPause();
  Log.d(TAG, "Fragment-onPause");
 }

 /*
  * Fragment がユーザに見えない状態です。
  * 
  * @see android.support.v4.app.Fragment#onStop()
  */
 @Override
 public void onStop() {
  super.onStop();
  Log.d(TAG, "Fragment-onStop");
 }

 /*
  * Fragment が扱う View などのコンポーネントに紐付いた各種リソースを開放するための状態 ここで Fragment への参照が View
  * やコンポーネントに残っていると、メモリリークを起こします。
  * 
  * @see android.support.v4.app.Fragment#onDestroyView()
  */
 @Override
 public void onDestroyView() {
  super.onDestroyView();
  Log.d(TAG, "Fragment-onDestroyView");
 }

 /*
  * Fragment が完全にメモリから破棄される直前の状態です。
  * 
  * @see android.support.v4.app.Fragment#onDestroy()
  */
 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.d(TAG, "Fragment-onDestroy");
 }

 /*
  * Fragment が Activity から切り離される状態です。
  * 
  * @see android.support.v4.app.Fragment#onDetach()
  */
 @Override
 public void onDetach() {
  super.onDetach();
  Log.d(TAG, "Fragment-onDetach");
 }

}
■MainActivity.java
package com.example.fragment5;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;

public class MainActivity extends FragmentActivity {
 public static String TAG = "MainActivity";

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

  Log.d(TAG, "Activity-onCreate");

  // Fragmentを管理するFragmentManagerを取得
  FragmentManager manager = getSupportFragmentManager();
  // 追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
  FragmentTransaction tx = manager.beginTransaction();

  // 1つ目のfragmentを生成
  MainFragment fragment1 = new MainFragment();
  Bundle bundle1 = new Bundle();
  bundle1.putString("title", "キタ――(゚∀゚)――!!");
  // フラグメントに渡す値をセット
  fragment1.setArguments(bundle1);

  // Fragment をスタックに追加する
  // メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
  // add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
  // Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
  tx.add(R.id.layout_fragment_1, fragment1, "layout_fragment_1");
  // tx.add(R.id.layout_fragment_2, fragment2, "layout_fragment_2");
  //
  tx.commit();
 }

 @Override
 public void onStart() {
  super.onStart();
  Log.d(TAG, "Activity-onStart");
 }

 @Override
 public void onRestart() {
  super.onRestart();
  Log.d(TAG, "Fragment-onRestart");
 }

 @Override
 public void onResume() {
  super.onResume();
  Log.d(TAG, "Activity-onResume");
 }

 @Override
 public void onPause() {
  super.onPause();
  Log.d(TAG, "Activity-onPause");
 }

 @Override
 public void onStop() {
  super.onStop();
  Log.d(TAG, "Activity-onStop");
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.d(TAG, "Activity-onDestroy");
 }

}
実行結果は次のようになります。
setRetainInstance(true);を入れる前の挙動は次のようになっていましたが、
そのうち、6、10番目が呼ばれなくなっていました。
(onDestroy、onCreate が呼ばれなくなる)

ただし、15番目でFragmentのonCreateが呼ばれてしまっていましたΣ(゚Д゚ υ) アリャ
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDestroy ← 呼ばれなくなる
7.Fragment-onDetach
8.Activity-onDestroy
9.Fragment-onAttach
10.Fragment-onCreate ← 呼ばれなくなる
11.Activity-onCreate
12.Fragment-onCreateView
13.Fragment-onActivityCreated
14.Fragment-onAttach
15.Fragment-onCreate ← 呼ばれてる(´・ω`・)エッ?
16.Fragment-onCreateView
17.Fragment-onActivityCreated
18.Fragment-onStart
19.Fragment-onStart
20.Activity-onStart
21.Activity-onResume
22.Fragment-onResume
23.Fragment-onResume
(´ε`;)ウーン…って悩んでいたら同じように悩んでる方をハヶ━m9( ゚д゚)っ━ン!!

【Android】Fragmentで画面を傾けた際に初期化しないようにsetRetainInstanceを使えって言うけど、うまく行かないときの対処法
フラグメントで回転すると、エラーになる。

原因的には上記の設定で、FragmentのonCreateなどは抑制できたけど、
ActivityのonCreateは実行されてしまいますので、
その結果、ActivityのonCreateで行っているFragmentの生成処理が実行されてしまい、
FragmentのonCreateが2度呼ばれているみたいでした。

なので、既にFragmentが作成されているかチェックをしてあげて、
ある場合には実行しないようにすることで回避(゚∀゚)キタコレ!!

次のチェックをMainActivityのonCreateを次のようにします。
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  Log.d(TAG, "Activity-onCreate");

  // Fragmentを管理するFragmentManagerを取得
  FragmentManager manager = getSupportFragmentManager();
  
  //既にFragmentが作成されているかチェック
  if (manager.findFragmentByTag("layout_fragment_1") == null) {
   // 追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
   FragmentTransaction tx = manager.beginTransaction();
 
   // 1つ目のfragmentを生成
   MainFragment fragment1 = new MainFragment();
   Bundle bundle1 = new Bundle();
   bundle1.putString("title", "キタ――(゚∀゚)――!!");
   // フラグメントに渡す値をセット
   fragment1.setArguments(bundle1);
 
   // Fragment をスタックに追加する
   // メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
   // add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
   // Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
   tx.add(R.id.layout_fragment_1, fragment1, "layout_fragment_1");
   // tx.add(R.id.layout_fragment_2, fragment2, "layout_fragment_2");
   //
   tx.commit();
  }
 }
その結果、画面回転時のライフサイクルが次のようになりました。
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDetach
7.Activity-onDestroy
8.Fragment-onAttach
9.Activity-onCreate
10.Fragment-onCreateView
11.Fragment-onActivityCreated
12.Fragment-onStart
13.Activity-onStart
14.Activity-onResume
15.Fragment-onResume
最初の頃に比べてだいぶスッキリ( ´∀`)bグッ!

そして、前回の「Fragmentを使ってみる(ライフサイクルについて)」で謎だった
Fragment画面回転時にonStartが2度実行されてしまう現象も解消されたっぽい!ワーイヽ(゚∀゚)メ(゚∀゚)メ(゚∀゚)ノワーイ
これが原因なのかな?o(゚Д゚ = ゚Д゚)o キョロキョロ

まとめるとFragmentの画面回転時のライフサイクルパターンとして次のケースがある。

1)Fragmentを単純に作成した場合
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDestroy
7.Fragment-onDetach
8.Activity-onDestroy
9.Fragment-onAttach
10.Fragment-onCreate
11.Activity-onCreate
12.Fragment-onCreateView
13.Fragment-onActivityCreated
14.Fragment-onAttach
15.Fragment-onCreate
16.Fragment-onCreateView
17.Fragment-onActivityCreated
18.Fragment-onStart
19.Fragment-onStart
20.Activity-onStart
21.Activity-onResume
22.Fragment-onResume
23.Fragment-onResume
2)FragmentでsetRetainInstance(true);を設定した場合 (onDestroy、onCreate が呼ばれなくなる)
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDetach
7.Activity-onDestroy
8.Fragment-onAttach
9.Activity-onCreate
10.Fragment-onCreateView
11.Fragment-onActivityCreated
12.Fragment-onAttach
13.Fragment-onCreate
14.Fragment-onCreateView
15.Fragment-onActivityCreated
16.Fragment-onStart
17.Fragment-onStart
18.Activity-onStart
19.Activity-onResume
20.Fragment-onResume
21.Fragment-onResume
3)Fragmentを作成時に、Activity側で作成済みのFragmentかチェックと、 setRetainInstance(true);を設定した場合
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDetach
7.Activity-onDestroy
8.Fragment-onAttach
9.Activity-onCreate
10.Fragment-onCreateView
11.Fragment-onActivityCreated
12.Fragment-onStart
13.Activity-onStart
14.Activity-onResume
15.Fragment-onResume
3番目が一番(・∀・)イイネ!!

以上です(`・ω・´)ゞビシッ!!

参考URL

Fragmentを使ってみる(ライフサイクルについて)

今回は、Fragmentのライフサイクルを確認したいと思います( ̄ー ̄)ニヤリ
基本的にアクティビティと同じみたいですが、一部追加されているものがあるみたいです。

各メソッドは次のようになっています。

基本的にはこちらの記事を参考にしてありますъ(゚Д゚)グッジョブ!!
2.02. Activity と Fragment

・onAttach(Activity)
Activityに関連付けされた際に一度だけ呼ばれる。
フラグメントがアクティビティと関連付けされたときに呼び出される(ここにActivityが渡される) 。

FragmentがActivityに対して何らかのコールバックを提供する場合、
Activity が必要なインタフェースを備えているかどうかチェックした方が良い。
public class MainFragment extends Fragment {
    private FragmentCallbacks mCallback;
    
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mCallback = (FragmentCallbacks) activity;
        } catch (ClassCastException e) {
            // Fragment が組み込まれる先の Activity に対して、FragmentCallbacks インタフェースの実装を要求する為
            // キャストに失敗した場合は、実行時例外としてプログラムのミスであることを示す 
            throw new IllegalStateException("activity should implement FragmentCallbacks", e);
        }
    }

    public static interface FragmentCallbacks {
        public void onHogehoge();
    }
}
・onCreate(Bundle)
Fragmentを構築する状態。
Fragmentがユーザに見える状態になるまで保持しておくべきコンポーネントの初期化を行う

・onCreateView(LayoutInflater, ViewGroup, Bundle)
Fragmentが持つViewを構築する状態
//Viewを持つFragmentの場合
public class MainFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // LayoutInflater を利用して、レイアウトをリソースとして読み込む
        View view = inflater.inflate(R.layout.fragment_main, container, false);
        return view;
    }
}
//Viewを持たないFragmentの場合
public class MainFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // View を持たない Fragment は、ここで null を返す
        return null;
    }
}
・onActivityCreated(Bundle)
ActivityのonCreateの状態の処理が終わったことを示す状態。

・onStart()
FragmentがActivity から切り離される状態。

・onResume()
FragmentのUIが構築され、ユーザとのインタラクションが出来るようになった状態。

・onPause()
ユーザが別の画面への遷移をしようとして Fragment から離れていこうとした状態です。
永続化するべき情報はここで保存させておく。
この状態になったからといって、Fragmentがメモリから破棄されるわけではない。

・onStop()
フォアグラウンドで無くなった場合に呼び出される。 (ユーザに見えない状態)

・onDestroyView()
View などのコンポーネントに紐付いた各種リソースを開放するための状態
ここでFragmentへの参照がViewやコンポーネントに残っていると、メモリリークを起こします。

・onDestroy()
Fragment が完全にメモリから破棄される直前の状態

・onDetach()
FragmentがActivityからの切り離される状態

公式ドキュメントの方には次のように載っていました。



実際に下記のプログラムで実行の流れを確認してみました(・д・)ジーッ
■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment4"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment4.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■fragment_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:background="#eeeeee"
    >
     
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        />
     
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        />
 
</LinearLayout>

■activity_main.xml
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <LinearLayout
        android:id="@+id/layout_fragment_1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:background="#ff0000"
        />
 
</LinearLayout>

■MainFragment.java
package com.example.fragment4;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainFragment extends Fragment {
 public static String TAG = "MainFragment";

 /*
  * Fragmentが Activityに対して何らかのコールバックを提供する場合、
  * Activityが必要なインタフェースを備えているかどうかチェックなどを行う。
  * 
  * @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
  */
 @Override
 public void onAttach(Activity act) {
  super.onAttach(act);
  Log.d(TAG, "Fragment-onAttach");
 }

 /*
  * Fragment がユーザに見える状態になるまで保持しておくべきコンポーネントの初期化。
  * 
  * @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.d(TAG, "Fragment-onCreate");
 }

 /*
  * Fragment が持つ View を構築する状態です。 View を持たない Fragment は、ここで null を返す
  * 
  * @see
  * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
  * android.view.ViewGroup, android.os.Bundle)
  */

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.d(TAG, "Fragment-onCreateView");
  // Bundleで保存されたデータを復元
  String title = getArguments().getString("title");

  //
  Log.d("onCreateView", title);

  // 第1引数:レイアウトXMLファイルのリソースID、
  // 第2引数:
  // 第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
  // trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
  View view = inflater.inflate(R.layout.fragment_main, container, false);
  //
  TextView text = (TextView) view.findViewById(R.id.text_view);
  text.setText(title);
  //
  return view;
 }

 /*
  * Activity の onCreate の状態の処理が終わったことを示す状態です。
  * 
  * @see android.support.v4.app.Fragment#onActivityCreated(android.os.Bundle)
  */
 @Override
 public void onActivityCreated(Bundle bundle) {
  super.onActivityCreated(bundle);
  Log.d(TAG, "Fragment-onActivityCreated");
 }

 /*
  * Fragment の UI が構築され、ユーザに見える状態です。
  * 
  * @see android.support.v4.app.Fragment#onStart()
  */
 @Override
 public void onStart() {
  super.onStart();
  Log.d(TAG, "Fragment-onStart");
 }

 /*
  * Fragment の UI が構築され、ユーザとのインタラクションが出来るようになった状態です。
  * 
  * @see android.support.v4.app.Fragment#onResume()
  */
 @Override
 public void onResume() {
  super.onResume();
  Log.d(TAG, "Fragment-onResume");
 }

 /*
  * ユーザが別の画面への遷移をしようとして Fragment から離れていこうとした状態です。 Activity
  * と同じく、この時点で、永続化するべき情報を保存するようにしておきます。 必ずしもこの後の状態へ遷移し、Fragment
  * がメモリから破棄されるわけではありません。
  * 
  * @see android.support.v4.app.Fragment#onPause()
  */
 @Override
 public void onPause() {
  super.onPause();
  Log.d(TAG, "Fragment-onPause");
 }

 /*
  * Fragment がユーザに見えない状態です。
  * 
  * @see android.support.v4.app.Fragment#onStop()
  */
 @Override
 public void onStop() {
  super.onStop();
  Log.d(TAG, "Fragment-onStop");
 }

 /*
  * Fragment が扱う View などのコンポーネントに紐付いた各種リソースを開放するための状態 ここで Fragment への参照が View
  * やコンポーネントに残っていると、メモリリークを起こします。
  * 
  * @see android.support.v4.app.Fragment#onDestroyView()
  */
 @Override
 public void onDestroyView() {
  super.onDestroyView();
  Log.d(TAG, "Fragment-onDestroyView");
 }

 /*
  * Fragment が完全にメモリから破棄される直前の状態です。
  * 
  * @see android.support.v4.app.Fragment#onDestroy()
  */
 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.d(TAG, "Fragment-onDestroy");
 }

 /*
  * Fragment が Activity から切り離される状態です。
  * 
  * @see android.support.v4.app.Fragment#onDetach()
  */
 @Override
 public void onDetach() {
  super.onDetach();
  Log.d(TAG, "Fragment-onDetach");
 }

}
■MainActivity.java
package com.example.fragment4;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;

public class MainActivity extends FragmentActivity {
 public static String TAG = "MainActivity";

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

  Log.d(TAG, "Activity-onCreate");

  // Fragmentを管理するFragmentManagerを取得
  FragmentManager manager = getSupportFragmentManager();
  // 追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
  FragmentTransaction tx = manager.beginTransaction();

  // 1つ目のfragmentを生成
  MainFragment fragment1 = new MainFragment();
  Bundle bundle1 = new Bundle();
  bundle1.putString("title", "キタ――(゚∀゚)――!!");
  // フラグメントに渡す値をセット
  fragment1.setArguments(bundle1);

  // Fragment をスタックに追加する
  // メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
  // add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
  // Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
  tx.add(R.id.layout_fragment_1, fragment1, "layout_fragment_1");
  // tx.add(R.id.layout_fragment_2, fragment2, "layout_fragment_2");
  //
  tx.commit();
 }

 @Override
 public void onStart() {
  super.onStart();
  Log.d(TAG, "Activity-onStart");
 }

 @Override
 public void onRestart() {
  super.onRestart();
  Log.d(TAG, "Fragment-onRestart");
 }

 @Override
 public void onResume() {
  super.onResume();
  Log.d(TAG, "Activity-onResume");
 }

 @Override
 public void onPause() {
  super.onPause();
  Log.d(TAG, "Activity-onPause");
 }

 @Override
 public void onStop() {
  super.onStop();
  Log.d(TAG, "Activity-onStop");
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.d(TAG, "Activity-onDestroy");
 }

}
実行結果は次のようになります。

特に何もしてないのでデバッグ用のログの方を確認してみます。
ログの出力結果は次のようになりましたφ(`д´)メモメモ...

■起動時に発生した流れ。
1.Activity-onCreate
2.Fragment-onAttach
3.Fragment-onCreate
4.Fragment-onCreateView
5.Fragment-onActivityCreated
6.Fragment-onStart
7.Activity-onStart
8.Activity-onResume
9.Fragment-onResume
想定どおりの動き(゚д゚)(。_。)(゚д゚)(。_。) ウンウン

■回転させた時に発生した流れ。
1.Fragment-onPause
2.Activity-onPause
3.Fragment-onStop
4.Activity-onStop
5.Fragment-onDestroyView
6.Fragment-onDestroy
7.Fragment-onDetach
8.Activity-onDestroy
9.Fragment-onAttach
10.Fragment-onCreate
11.Activity-onCreate
12.Fragment-onCreateView
13.Fragment-onActivityCreated
14.Fragment-onAttach
15.Fragment-onCreate
16.Fragment-onCreateView
17.Fragment-onActivityCreated
18.Fragment-onStart
19.Fragment-onStart
20.Activity-onStart
21.Activity-onResume
22.Fragment-onResume
23.Fragment-onResume

あれ、何か微妙に挙動が想像と違う:(;゙゚'ω゚'):
FragmentのonStartが2回呼ばれてしまっているのはなぜっ!(´ε`;)ウーン…

というか、その前にonAttach、onCreate、onCreateViewが2回呼ばれてしまっている。
エミュレーターだからかな?と思い実機でやってみたけど同じ現象でした・・・((((;゚Д゚))))ガクガクブルブル

これが通常の動作なのかな?( ゚д゚)

だとしたら初期化を1度しか行いたいたくない時とか、
別途処理を追加する必要があるのかな~?と思いながらgoogle先生に聞いてみたら、
Fragmentの初期化(onCreate、onCreateView)を1度しかやらせない方法はあるそうでした。
次はその手順を書きたいと思います(`・ω・´)シャキーン

以上です(`・ω・´)ゞビシッ!!

参考URL

2013年10月28日月曜日

Fragmentを使ってみる(レイアウトなし)

引き続き絶賛Fragmentの勉強中(`・ω・´)シャキーン

今回はレイアウトが無いFragmentを作成したいと思います。
レイアウトを利用しないため、addメソッドでリソースIDを関連付ける必要がありませんφ(`д´)メモメモ...
そのため、onCreateViewの呼び出しが行われないので、オーバーライドする必要が無いそうです。

違うアクティビティーなどで共通のロジックを利用する場合や、
なんかバックグラウンドで動かす処理の場合に役に立つカモ――(゚∀゚)――!!?

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment3"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment3.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■activity_main.xml
メインのレイアウトファイルになります。
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
</LinearLayout>
■MainFragment.java

Fragmentのソースコードになります。
って、レイアウトと関連付けする必要がないので中身は空ですワーイヽ(゚∀゚)メ(゚∀゚)メ(゚∀゚)ノワーイ
package com.example.fragment3;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainFragment extends Fragment {
}
■MainActivity.java

メインではFragmentをいつも通り生成しますが、
add時にFragmentと関連付けるレイアウトのリソースIDを指定せず、
第1引数に、追加されるFragmentを設定して(事前に Activity に追加されていてはならない)、
第2引数に、Fragmentのタグ名を指定する。(ActivityからFragmentを取得したい場合はFragmentManager.findFragmentByTagで取り出し時に利用される)

記述は簡単!(`・ω・´)シャキーン
package com.example.fragment3;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
 
public class MainActivity extends FragmentActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //Fragmentを管理するFragmentManagerを取得
        FragmentManager manager = getSupportFragmentManager();
        //追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
        FragmentTransaction tx = manager.beginTransaction();

        //fragmentを生成
        MainFragment fragment1 = new MainFragment();
        //
        tx.add(fragment1, "layout_fragment_1");
        // 
        tx.commit();
    }
 
}
実行結果は次のようになります。


何も処理をしていないので真っ白(ΦωΦ)フフフ…

(´ε`;)ウーン…便利なのは分かっているのですが、
いまいちパッと使いどころが思いつかないのは、まだまだ未熟なせいか・・・orz

以上です(`・ω・´)ゞビシッ!!

参考URL

Fragmentを使ってみる(動的に追加&引数を渡す)

前回の「Fragmentを使ってみる」に続き、Fragmentド━━━━m9(゚∀゚)━━━━ン!!
今回は動的にFragmentを追加してみたいと思います( ̄ー ̄)ニヤリ
(レイアウトXMLで定義したFragmentはreplaceなどで切り替えが行えないので、
動的にコードからaddなどを利用する必要がある。)

ついでに引数も渡してみたいと思います( ̄ー ̄)ニヤリ

Fragmentに引数を渡したい場合にはこちらの方法でも渡せます!こっちのが良いかもです!
Fragmentを使ってみる(ファクトリメソッドを利用して引数を渡す)

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■fragment_main.xml
fragmentとは前回と同じレイアウトを利用します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:background="#eeeeee"
    >
     
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        />
     
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        />
 
</LinearLayout>

■activity_main.xml
メインのレイアウトでは、Fragmentの挿入先として、
LinearLayoutの「layout_fragment_1」、「layout_fragment_2」を用意しておきます。
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <LinearLayout
        android:id="@+id/layout_fragment_1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:background="#ff0000"
        />
 
    <LinearLayout
        android:id="@+id/layout_fragment_2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical"
        android:background="#00ff00"
         />
 
</LinearLayout>

■MainFragment.java
メインのActivityで設定されたBundleからデータを取得して、
TextViewに設定を行います。

Fragmentはインスタンス生成時に引数を渡す事が出来ないそうです。
なぜかというと、Fragmentがメモリが足らなくなって破棄された場合などで、
再度Fragmentが構築される際に空のコンストラクタから
Fragmentのインスタンスを再生成するためらしいです。ガ━━(;゚Д゚)━━ン!!
※そのため空のコンストラクタが用意されていない場合には、上記の状態になるとアプリが落ちるそうです。

こういう事情もあり、Bundleを利用して引数を設定することで、
再生成のときに復元をしてくれるようになります。
package com.example.fragment2;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainFragment extends Fragment {

    /**
     * Viewの生成
     * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     
        //Bundleで保存されたデータを復元
     String title = getArguments().getString("title");

     //
        Log.d("onCreateView", title);
        
     //第1引数:レイアウトXMLファイルのリソースID、
     //第2引数:
     //第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
     //       trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
     View view = inflater.inflate(R.layout.fragment_main, container, false);
     //
        TextView text = (TextView)view.findViewById(R.id.text_view);
        text.setText(title);
     //
        return view;
    }

}
■MainActivity.java

メインActivityのソースコードになります。

動的にFragmentを追加するには、FragmentManagerを利用します。

こちらのサイトから抜粋させて頂きましたъ(゚Д゚)グッジョブ!!
Android Fragment を使う
FragmentManager でできることは次のようなことになります。
・findFragmentById() もしくは findFragmentByTag() で Activity に存在する Fragment を取得
・popBackStack() でバックスタックの Fragment を pop off (ユーザーが BACK ボタンを押したときと同じ動作)
・addOnBackStackChangedListener() を使って、バックスタックの変更用リスナーを登録
・FragmentTransaction を開く

Fragment Transactionについて
・各トランザクションは一度に行いたい変更の組み合わせ
・実行したいすべてのトランザクションを add(), remove(), replace() などを使ってセットし、それから commit() を呼び出してトランザクションを適用させる
・例えば、以前の状態をバックスタックに保存して、別のフラグメントに置き換えるにはこんな感じになる

Fragmentの引数について
BundleをFragmentのsetArgumentsに渡す事でFragmentに値を渡します。
その際の注意点として次の点があるそうです。
こちらのサイトから抜粋
【Android 実装の罠 #1】 Fragment#setArguments()
・setArguments()は、FragmentManagerによってAddされた以降は呼んではいけない
・setArguments()呼び出し時に、Fragment#mIndexの評価を行っている。0以上であればIllegalStateExceptionをスローする
・mIndexは、FragmentManager#makeActive()呼び出し時に入力される
package com.example.fragment2;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
 
public class MainActivity extends FragmentActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //Fragmentを管理するFragmentManagerを取得
        FragmentManager manager = getSupportFragmentManager();
        //追加や削除などを1つの処理としてまとめるためのトランザクションクラスを取得
        FragmentTransaction tx = manager.beginTransaction();
        
        //1つ目のfragmentを生成
        MainFragment fragment1 = new MainFragment();
        Bundle bundle1 = new Bundle();
        bundle1.putString("title", "キタ――(゚∀゚)――!!");
        // フラグメントに渡す値をセット
        fragment1.setArguments(bundle1);

        //2つ目のfragmentを生成
        MainFragment fragment2 = new MainFragment();
        Bundle bundle2 = new Bundle();
        bundle2.putString("title", "クル━━━━(゚∀゚)━━━━!!");
        fragment2.setArguments(bundle2);
        
        //Fragment をスタックに追加する
        //メインレイアウトに対して追加先のビューのID、Fragment、Fragmentのタグ。
        //add() したときに既にバックスタックに同じタグの Fragment が存在する場合、
        //Fragment は新規作成されず、既にインスタンス化してある Fragment が再表示される。
        tx.add(R.id.layout_fragment_1, fragment1, "layout_fragment_1");
        tx.add(R.id.layout_fragment_2, fragment2, "layout_fragment_2");
        // 
        tx.commit();
    }
 
}
実行結果は次のようになります。



以上です(`・ω・´)ゞビシッ!!

参考URL

Fragmentを使ってみる

ViewPagerを利用している際に、Framentが(・∀・)イイ!!という情報を(σ・∀・)σゲッツ!!したので使ってみました(`・ω・´)シャキーン

■Fragmentについて
・Android 3.0 (APIレベル11) から導入されたコンポーネント。
・Support PackageでサポートされているのでAndroid 1.6 (APIレベル4) から使用可能。
・Activityと同じくライフサイクルを持っている。(Activitiに依存している)
・サブクラスとして、次のものがある。
DialogFragment ダイアログを提供するために拡張されたクラス
ListFragment 一覧(ListView)の表示等を提供するために拡張されたクラス
PreferenceFragment アプリケーションの設定情報を容易に扱えるように拡張されたクラス
WebViewFragment ブラウザ(WebView)の表示を提供するために拡張されたクラス
FragmentBreadCrumbs パン屑リスト
FragmentTabHost タブ用

他にはこちらに詳しく載っていましたので抜粋させて頂きましたъ(゚Д゚)グッジョブ!!
Fragment を使ってみよう
Fragmentの特徴
・View + ロジックの再利用可能なコンポーネントを作れる。
・Viewを持たない、ロジックだけの再利用可能なコンポーネントも作れる。
・Activityのライフサイクルと関連するライフサイクルを持つ
・バックスタック機能
・1つのViewのようにレイアウトXMLファイルにかける(タグ)
・複数のViewをも持ってそれぞれをコントロールできる。

Fragmentのいまいちな所
・カスタムViewと同じように完全修飾名でレイアウトXMLファイルに書くので...
・グラフィカルエディタで正確に再現できない場合がある。
・クラス名がレイアウトエディタ上で自動補完されないので、タイポによるエラーを起こしやすい

どこまでこれまのでViewとActivityにやらせて、どこを新しくFragmentにやらせばいいの?

・View or カスタムView
描画とそのための最低限のロジック

・Framgment
View+そのViewに関連するロジック(Viewの操作やViewに表示するデータの処理)

・Acitivity
それ以外。ActivityとFragment間のデータの受け渡し、複数のFragment間のデータの受け渡し、Windowの設定などActivityに関連するロジック
色々利点があるそうなので早速使ってみる(ΦωΦ)フフフ…
■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.fragment2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■fragment_main.xml
fragment用のレイアウトになります。
単純にTextViewとImageViewを配置したものにします。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:background="#eeeeee"
    >
     
    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        />
     
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        />
 
</LinearLayout>
■activity_main.xml
メインのアクティビティ用のレイアウトになります。
注意点として、classに記述するFragmentの完全修飾名のクラスパスになります:(;゙゚'ω゚'):
<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.example.fragment1.MainFragment" />

■MainFragment.java
Fragmentのソースになります。
package com.example.fragment1;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainFragment extends Fragment {
 
 /**
  * 
  * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
  */ 
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  
     //第1引数:レイアウトXMLファイルのリソースID、
     //第2引数:
     //第3引数:trueにするかfalseにするかで戻り値となるルートビュー(View)が変わる。
     //       trueの場合には、第2引数で渡したViewGrou、falseの場合には第1引数で渡したリソースIDがルートビューになる
     View view = inflater.inflate(R.layout.fragment_main, container, false);
     //TextViewを取得する
     TextView text = (TextView)view.findViewById(R.id.text_view);
     //文字列を設定する
     text.setText("(゚∀゚)キタコレ!!");
     //
        return view;
 }
}
■MainActivity.java
メインのソースコードになります。
package com.example.fragment1;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
 
public class MainActivity extends FragmentActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
}


これだと見た目上は何が違うのかさっぱり(´・ω・`)ショボーン

今後はFragmentを利用して開発していくなり(ΦωΦ)フフフ…
以上です(`・ω・´)ゞビシッ!!

参考URL

2013年10月27日日曜日

ViewPagerを使ってみる(動的に更新・追加・削除)

前回のViewPagerを使ってみるの続きでViewPagerについてになります。

前回は初期化されたアダプターのデータを表示させただけでした、
今度は動的にViewPagerのビューの中身を変更したり、
ビューの更新、追加を行いたいと思います。(`・ω・´)シャキーン
(削除は微妙になってしまいました・・・orz)

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.viewpager2"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.viewpager2.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
■res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">ViewPager</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="prev">前へ</string>
    <string name="next">次へ</string>
    <string name="first">最初へ</string>
    <string name="last">最後へ</string>
    <string name="change">変更</string>
    <string name="add">追加</string>
    <string name="delete">削除</string>
</resources>

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" >
        
        <Button
            android:id="@+id/btn_change"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/change"
            android:text="@string/change"/>
        
        <Button
            android:id="@+id/btn_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/add"
            android:text="@string/add"/>

        <Button
            android:id="@+id/btn_delete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/delete"
            android:text="@string/delete"/>

    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" >

        <Button
            android:id="@+id/btn_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/first"
            android:text="@string/first"/>

        <Button
            android:id="@+id/btn_prev"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/prev"
            android:text="@string/prev"/>

        <Button
            android:id="@+id/btn_next"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/next"
            android:text="@string/next"/>

        <Button
            android:id="@+id/btn_last"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/last"
            android:text="@string/last"/>
    </LinearLayout>

</LinearLayout>

■MainActivity.java
注意点としては、アダプターを追加・削除した後には、
notifyDataSetChangedを実行してアダプターを更新してあげないといけないみたいです。
※ViewPagerのビューの内容の変更時には実行する必要がない。

これを実行せずにViewPagerをスワイプなどして動かすと次のエラーが発生してしまうようでした(´;ω;`)ブワッ
java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 7, found: 8 Pager id: com.example.viewpager2:id/viewpager Pager class: class android.support.v4.view.ViewPager Problematic adapter: class com.example.viewpager2.CustomPagerAdapter
package com.example.viewpager2;

import java.util.ArrayList;

import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.SimpleOnPageChangeListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

 private ArrayList<Button> mButtonList = new ArrayList<Button>();
 private ViewPager mViewPager = null;
 private CustomPagerAdapter mPagerAdapter;
 private int mCurrentPageIndex = 0;

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

  // 各ボタンを保持する
  mButtonList.add((Button) findViewById(R.id.btn_start));
  mButtonList.add((Button) findViewById(R.id.btn_prev));
  mButtonList.add((Button) findViewById(R.id.btn_next));
  mButtonList.add((Button) findViewById(R.id.btn_last));
  mButtonList.add((Button) findViewById(R.id.btn_change));
  mButtonList.add((Button) findViewById(R.id.btn_add));
  mButtonList.add((Button) findViewById(R.id.btn_delete));
  // ボタンにクリックイベントを設定する
  for (Button btn : mButtonList) {
   btn.setOnClickListener(this);
  }
  // ViewPagerを取得する
  mViewPager = (ViewPager) findViewById(R.id.viewpager);
  // アダプターを設定する
  mPagerAdapter = new CustomPagerAdapter(this);
  // アダプター経由でViewPagerに追加する
  mPagerAdapter.add(Color.BLACK);
  mPagerAdapter.add(Color.RED);
  mPagerAdapter.add(Color.GREEN);
  mPagerAdapter.add(Color.BLUE);
  mPagerAdapter.add(Color.CYAN);
  mPagerAdapter.add(Color.MAGENTA);
  mPagerAdapter.add(Color.YELLOW);

  // リスナーの設定
  mViewPager.setOnPageChangeListener(new PageChangeListener());
  // アダプターを設定する
  mViewPager.setAdapter(mPagerAdapter);
 }

 /**
  * ViewPagerのリスナー用のクラス
  */
 class PageChangeListener extends SimpleOnPageChangeListener {

  /**
   * 移動中に発生する。
   * 
   * @see android.support.v4.view.ViewPager.SimpleOnPageChangeListener#onPageScrolled(int,
   *      float, int)
   */
  @Override
  public void onPageScrolled(int position, float positionOffset,
    int positionOffsetPixels) {
   Log.d("PageChangeListener.onPageScrolled",
     String.valueOf(position) + ":"
       + String.valueOf(positionOffset) + ":"
       + String.valueOf(positionOffsetPixels));
  }

  /**
   * 現在表示しているViewの番号
   * 
   * @see android.support.v4.view.ViewPager.SimpleOnPageChangeListener#onPageSelected(int)
   */
  @Override
  public void onPageSelected(int position) {
   // Page change Operation!
   Log.d("PageChangeListener.onPageSelected", String.valueOf(position));
   mCurrentPageIndex = position;

  }

  /**
   * ViewPagerの状態の検知
   * 
   * @see android.support.v4.view.ViewPager.SimpleOnPageChangeListener#onPageScrollStateChanged(int)
   */
  @Override
  public void onPageScrollStateChanged(int state) {
   Log.d("PageChangeListener.onPageScrollStateChanged",
     String.valueOf(state));
   if (state == ViewPager.SCROLL_STATE_SETTLING) {
    int page = mViewPager.getCurrentItem();
    /*
     * textView.setText(String.valueOf(page));
     */
   }
  }
 }

 /*
  * ボタンクリック時の動作
  */
 public void onClick(View v) {

  Log.d("ボタン", (String) v.getTag());
  //
  Resources res = getResources();
  //
  if (v.getTag().equals((String) res.getString(R.string.first))) {
   mViewPager.setCurrentItem(0);
  } else if (v.getTag().equals((String) res.getString(R.string.last))) {
   mViewPager.setCurrentItem(mPagerAdapter.getCount() - 1);
  } else if (v.getTag().equals((String) res.getString(R.string.prev))) {
   mViewPager.arrowScroll(View.FOCUS_LEFT);
   // mViewPager.setCurrentItem(mViewPager.getCurrentItem() - 1);
  } else if (v.getTag().equals((String) res.getString(R.string.next))) {
   mViewPager.arrowScroll(View.FOCUS_RIGHT);
   // mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
  } else if (v.getTag().equals((String) res.getString(R.string.change))) {
   onPagerChange();
  } else if (v.getTag().equals((String) res.getString(R.string.add))) {
   onPagerAdd();
  } else if (v.getTag().equals((String) res.getString(R.string.delete))) {
   onPagerDelete();
  }
 }

 /**
     * 更新する
     */
 public void onPagerChange() {
  Log.d("onPagerChange", "");
  mPagerAdapter.changeColor(getColor());
 }

 /**
     * 追加する
     */
 public void onPagerAdd() {
  Log.d("onPagerAdd", "");
  // ランダムに色を設定する
  mPagerAdapter.add(getColor());
  // アダプターの更新を通知する
  mPagerAdapter.notifyDataSetChanged();
 }

 /**
     * 削除する
     */
 public void onPagerDelete() {
  Log.d("onPagerDelete", "");
  mPagerAdapter.remove(mCurrentPageIndex);
  //
  mPagerAdapter.notifyDataSetChanged();
 }

 /**
     * カラーコードを生成する
     */
 public static int getColor() {
  // ランダムに色を追加する
  int R = (int) (Math.random() * 256);
  int G = (int) (Math.random() * 256);
  int B = (int) (Math.random() * 256);
  return Color.rgb(R, G, B);
 }
}
■CustomPagerAdapter.java
package com.example.viewpager2;

import java.util.ArrayList;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class CustomPagerAdapter extends PagerAdapter {

 /** コンテキスト. */
 private Context mContext;

 /** リスト. */
 private ArrayList<Integer> mList;

 public TextView primaryLayout;

 /**
  * コンストラクタ.
  */
 public CustomPagerAdapter(Context context) {
  mContext = context;
  mList = new ArrayList<Integer>();
 }

 /**
  * リストにアイテムを追加する.
  * 
  * @param item
  *            アイテム
  */
 public void add(Integer item) {
  Log.d("CustomPagerAdapter", "add");
  mList.add(item);

 }

 /**
  * アイテムを追加するときに呼ばれる。このメソッド内で View をコンテナに追加する。
  * 
  * @see android.support.v4.view.PagerAdapter#instantiateItem(android.view.ViewGroup,
  *      int)
  */
 @Override
 public Object instantiateItem(ViewGroup container, int position) {
  Log.d("CustomPagerAdapter", "instantiateItem");
  // リストから取得
  Integer item = mList.get(position);

  // View を生成
  TextView textView = new TextView(mContext);
  textView.setText("Page:" + position);
  textView.setTextSize(30);
  textView.setTextColor(item);
  textView.setGravity(Gravity.CENTER);

  // コンテナに追加
  container.addView(textView);
  return textView;
 }

 /**
  * アイテムを削除するときに呼ばれる。このメソッド内で View の削除をおこなう。 postion番目のViweを削除するために利用
  * 
  * @param container
  *            : 削除するViewのコンテナ
  * @param position
  *            : インスタンス削除位置
  * @param object
  *            : instantiateItemメソッドで返却したオブジェクト
  * @see android.support.v4.view.PagerAdapter#destroyItem(android.view.ViewGroup,
  *      int, java.lang.Object)
  */
 @Override
 public void destroyItem(ViewGroup container, int position, Object object) {
  // コンテナから View を削除
  container.removeView((View) object);
 }

 /**
  * ViewPager に登録する全アイテム数を返す。
  * 
  * @see android.support.v4.view.PagerAdapter#getCount()
  */
 @Override
 public int getCount() {
  // リストのアイテム数を返す
  return mList.size();
 }

 /**
  * Object に View が含まれているか判定する。 第2引数のobjectに第1引数のviewが含まれているか、判定する。
  * 正しく比較をしないと挙動がおかしくなった・・・orz スワイプ中のページ送りのフォーカスや表示調整に使っている?
  * 
  * @see android.support.v4.view.PagerAdapter#isViewFromObject(android.view.View,
  *      java.lang.Object)
  */
 @Override
 public boolean isViewFromObject(View view, Object object) {
  // Object 内に View が存在するか判定する
  // コンテナに入っているViewを取り出して個別に比較する処理
  return view == (TextView) object;
  // return view.equals(object);
 }

 /**
  * 現在表示してるViewを取得する
  * 
  * @see android.support.v4.view.PagerAdapter#setPrimaryItem(android.view.ViewGroup,
  *      int, java.lang.Object)
  */
 @Override
 public void setPrimaryItem(ViewGroup container, int position, Object object) {
  primaryLayout = (TextView) object;
  super.setPrimaryItem(container, position, object);
 }
 /**
  * この記述をしておくとViewPagerのビューを更新してくれる?
  * これを書いておくとアダプターで要素をremoveした後に更新を正しくしてくれるようになった。(追加には関係ない)
  * 
  * @see android.support.v4.view.PagerAdapter#getItemPosition(java.lang.Object)
  */
 @Override
 public int getItemPosition(Object object) {
  return POSITION_NONE;
 }
 /**
  * 表示されているViewを取得する
  * 
  * @return 表示されているView
  */
 public TextView getPrimaryView() {
  return primaryLayout;
 }

 /**
  * 色を変更する
  */
 public void changeColor(int item) {

  TextView viewText = getPrimaryView();
  viewText.setTextColor(item);

 }

 /**
     * 
     */
 public void remove(int position) {
  if (position < 0 || position >= mList.size()) {
   return;
  }
  Log.d("remove", String.valueOf(position));
  mList.remove(position);
 }

}
削除の挙動が変な感じ・・・(; ・`д・´)
表示されているViewを消してるけど、そのタイミングでは消えていなくて、
ViewPagerを動かすと反映される感じ(´;ω;`)ウッ…

ただし消し方ってどうやるんだろう?

追記。こちらのViewPagerでViewを強制的に更新する方法のように
getItemPositionをオーバーライドして、POSITION_NONEを返したら、
消した直後に更新されるようになった(゚д゚)!



以上です(`・ω・´)ゞビシッ!!

参考URL

2013年10月26日土曜日

ViewPagerを使ってみる

スワイプやフリックで画像の切り替えなどを行うために、
きっとそれようのViewが既にあるはず!という事で調べたらViewSwitcher、ViewFliper、ViewPagerを見つけました(`・ω・´)シャキーン

どちらでもスワイプに対応してそうでしたが、
ViewPagerの方がiOSライク的に動くみたいなことが書かれていたので
今回はこちらを利用したいと思います!

■ViewPager
Androidの4.1.2から導入されたものらしいです。
サポートライブラリのandroid-support-v4.jarを利用すれば
Android 1.6から利用が可能とのこと(´・∀・`)ヘー
ViewPagerはメモリの節約のために、現在表示しているViewと前後のViewのみを生成して、それ以外は開放していくそうです。

ViewSwitcherやViewFlipperと違いはこちらから抜粋
また、ViewSwitcherやViewFlipperと違い、最後のView(最初のView)を表示しているときに、
次のView(前のView)を表示することはできません。
※ViewSwitcherやViewFlipperでは、最後のView(最初のView)でshowNext(showPrev)を呼ぶと、
最初(最後)のViewを表示することができます。

無限ループ的な感じでは作れないみたいですね・・・(´;ω;`)ウッ…
ただこちらの方がそれが出来るようにしてるっぽいですね~ъ(゚Д゚)グッジョブ!!
Android ループできる ViewPager (完全版)作った

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.viewpager"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.viewpager.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

■res/values/strings.xml
ボタンに設定している文字列を定義になります。
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">ViewPager</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="prev">前へ</string>
    <string name="next">次へ</string>
    <string name="first">最初へ</string>
    <string name="last">最後へ</string>

</resources>
■activity_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" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="スライドスクロール"
        android:textSize="28sp" />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center" >

        <Button
            android:id="@+id/btn_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/first"
            android:text="@string/first"/>

        <Button
            android:id="@+id/btn_prev"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/prev"
            android:text="@string/prev"/>

        <Button
            android:id="@+id/btn_next"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/next"
            android:text="@string/next"/>

        <Button
            android:id="@+id/btn_end"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:tag="@string/last"
            android:text="@string/last"/>
    </LinearLayout>

</LinearLayout>

■MainActivity.java
メインのアクティビティになります。
package com.example.viewpager;

import java.util.ArrayList;

import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

 private ArrayList<Button> mButtonList = new ArrayList<Button>();
 private ViewPager mViewPager = null;
 private CustomPagerAdapter mPagerAdapter;
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  //各ボタンを保持する
  mButtonList.add((Button) findViewById(R.id.btn_start));
  mButtonList.add((Button) findViewById(R.id.btn_prev));
  mButtonList.add((Button) findViewById(R.id.btn_next));
  mButtonList.add((Button) findViewById(R.id.btn_end));
  
  //ボタンにクリックイベントを設定する
  for (Button btn : mButtonList) {
   btn.setOnClickListener(this);
  }
  //ViewPagerを取得する
  mViewPager = (ViewPager) findViewById(R.id.viewpager);
  //アダプターを設定する
  mPagerAdapter = new CustomPagerAdapter(this);
  //アダプター経由でViewPagerに追加する
  mPagerAdapter.add(Color.BLACK);
  mPagerAdapter.add(Color.RED);
  mPagerAdapter.add(Color.GREEN);
  mPagerAdapter.add(Color.BLUE);
  mPagerAdapter.add(Color.CYAN);
  mPagerAdapter.add(Color.MAGENTA);
  mPagerAdapter.add(Color.YELLOW);
  //アダプターを設定する
  mViewPager.setAdapter(mPagerAdapter);
 }

    /*
     * ボタンクリック時の動作 
     */
    public void onClick(View v) {
     
        Log.d("ボタン",(String) v.getTag());
        //
        Resources res = getResources();
        //
        if (v.getTag().equals((String)res.getString(R.string.first))){
         mViewPager.setCurrentItem(0);
        }else if (v.getTag().equals((String)res.getString(R.string.last))){
         mViewPager.setCurrentItem(mPagerAdapter.getCount() - 1);
        }else if (v.getTag().equals((String)res.getString(R.string.prev))){
         mViewPager.arrowScroll(View.FOCUS_LEFT);
        }else if (v.getTag().equals((String)res.getString(R.string.next))){
         mViewPager.arrowScroll(View.FOCUS_RIGHT);
        }
    }

}

■CustomPagerAdapter.java
ViewPagerのアダプターの定義になります。
package com.example.viewpager;

import java.util.ArrayList;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class CustomPagerAdapter extends PagerAdapter {
 
    /** コンテキスト. */
    private Context mContext;
     
    /** リスト. */
    private ArrayList<Integer> mList;
 
    /**
     * コンストラクタ.
     */
    public CustomPagerAdapter(Context context) {
        mContext = context;
        mList = new ArrayList<Integer>();
    }
 
    /**
     * リストにアイテムを追加する.
     * @param item アイテム
     */
    public void add(Integer item) {
     Log.d("CustomPagerAdapter", "add");
        mList.add(item);
    }
 /*
  * アイテムを追加するときに呼ばれる。このメソッド内で View をコンテナに追加する。
  * @see android.support.v4.view.PagerAdapter#instantiateItem(android.view.ViewGroup, int)
  */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
     Log.d("CustomPagerAdapter", "instantiateItem");
        // リストから取得
        Integer item = mList.get(position);
 
        // View を生成
        TextView textView = new TextView(mContext);
        textView.setText("Page:" + position);
        textView.setTextSize(30);
        textView.setTextColor(item);
        textView.setGravity(Gravity.CENTER);
 
        // コンテナに追加
        container.addView(textView);
 
        return textView;
    }
 /**
  * アイテムを削除するときに呼ばれる。このメソッド内で View の削除をおこなう。 postion番目のViweを削除するために利用
  * 
  * @param container
  *            : 削除するViewのコンテナ
  * @param position
  *            : インスタンス削除位置
  * @param object
  *            : instantiateItemメソッドで返却したオブジェクト
  * @see android.support.v4.view.PagerAdapter#destroyItem(android.view.ViewGroup,
  *      int, java.lang.Object)
  */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // コンテナから View を削除
        container.removeView((View) object);
    }
 /*
  * ViewPager に登録する全アイテム数を返す。
  * @see android.support.v4.view.PagerAdapter#getCount()
  */
    @Override
    public int getCount() {
        // リストのアイテム数を返す
        return mList.size();
    }
 /**
  * Object に View が含まれているか判定する。 第2引数のobjectに第1引数のviewが含まれているか、判定する。
  * 正しく比較をしないと挙動がおかしくなった・・・orz スワイプ中のページ送りのフォーカスや表示調整に使っている?
  * 
  * @see android.support.v4.view.PagerAdapter#isViewFromObject(android.view.View,
  *      java.lang.Object)
  */ 
    @Override
    public boolean isViewFromObject(View view, Object object) {
        // Object 内に View が存在するか判定する
        return view == (TextView) object;
    }
}


以上です(`・ω・´)ゞビシッ!!

参考URL