2013年11月6日水曜日

Fragmentを使ってみる(置換する)

今回はFragmentの置換を行いたいと思います( ̄ー ̄)ニヤリ

サンプルの動作は、前回作成したものに対して、置換処理だけを追加しました。
(バックグラウンドの色変更も無くしました。)

実際に動かしてみると、何か変な挙動だな~と思っていたら、
置換処理は、今表示されているFragmentを削除してから、追加するという挙動になるそうです(´・∀・`)ヘー

そのため、今回のようにFrameLayoutを利用して重ねて表示する場合で、
追加ボタンを2回押して、Fragmentを2個重ねている状態で、
置換ボタンを押すと、現在表示中のFragmentが消されたタイミングで
一瞬下のFragmentが表示されてしまうっぽいです。

対策として、現在表示しているFragmentをhideで全て消すか、
背景を塗りつぶす方法などがあるみたいです。

ちなみに、今回は試していません(∀`*ゞ)テヘッ

こちらなどに詳しい対処法などが書いてありましたъ(゚Д゚)グッジョブ!!

Support Packageを使ってFragmentを利用する
注意点として、ft.replaceした場合、下にあるFragmentの内容が見えてしまいます。
(背景が透明になる)
したがって、背景を画像や色を指定して、下のFragmentが見えないようにしないと
いけません。(これはSupport Packageのみの現象?)
しかも、ListFragmentを利用した時(他のFragmentでも?)、画面をタッチすると、
下に配置されたFragmentもタッチを検出しますので、処理が実行されてしまいます。
BackStackに積む前に下のFragmentの処理が実行されないようにEnabled = falseにするか、
getFragmentManager().getBackStackEntryCount()をチェックして、積まれていたら
処理をスキップするなど工夫をしないといけないようです。

日本Androidの会 中国支部資料
1.回転を固定
<activity android:name="MainActivity" android:label="@string/app_name" android:screenOrientation="portrait"/> または
@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
 setContentView(R.layout.main);
}
2.背景を塗りつぶす
<LinearLayout 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@android:color/white" 
 android:orientation="vertical" >
3.再表示時に再度hideを設定する
@Override
public void onActivityCreated(Bundle savedInstanceState) {
 super.onActivityCreated(savedInstanceState);
 if (savedInstanceState != null) {
  boolean isHiddin = savedInstanceState.getBoolean("isHidden");
  if (isHiddin) {
   FragmentTransaction t = getFragmentManager().beginTransaction();
   t.hide(this);
   t.commit();
  }
 }
}
@Override
public void onSaveInstanceState(Bundle outState) {
 // TODO Auto-generated method stub
 super.onSaveInstanceState(outState);
 outState.putBoolean("isHidden", isHidden());
}

■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fragment8"
    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.fragment8.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" />

    <Button
        android:id="@+id/button_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:background="#000000"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/layout_fragment_top"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_weight="1"
        android:background="#ff0000"
        android:orientation="vertical" />
    <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1" />
</LinearLayout>
■MainFragmentTop.java
package com.example.fragment8;

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.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainFragmentTop extends Fragment {

 public final static String TAG = "MainFragmentTop";

 private TextView mTextView;
 private Button mButtonView;
 // コールバック
 private FragmentTopCallback mCallback;

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

  public void onClick(TextView v);
  public void onClick(Button button);

 }

 /*
  * 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がルートビューになる
  LinearLayout view = (LinearLayout) inflater.inflate(R.layout.fragment_main, container, false);
  // FragmentのTextViewの取得
  mTextView = (TextView) view.findViewById(R.id.text_view);
  // テキストを挿入
  mTextView.setText(title);
  // タグを設定する
  mTextView.setTag(TAG);
  
  // FragmentのImageViewの取得
  mButtonView = (Button) view.findViewById(R.id.button_view);
  //
  mButtonView.setText("追加");
  // 画像クリック時のイベントリスナー
  mButtonView.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if (mCallback != null) {
     mCallback.onClick(mTextView);
    }
   }
  });
  
  //
  final Button button = new Button(getActivity());
  // タグを設定する
  button.setTag(TAG);
  button.setText("置換");
  // 画像クリック時のイベントリスナー
  button.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if (mCallback != null) {
     mCallback.onClick(button);
    }
   }
  });
  
  //
  view.addView(button);
  //
  return view;
 }

 /*
  * TextViewの設定
  */
 public void setText(String text){
  mTextView.setText(text);
 }

}
■MainFragmentBottom.java
package com.example.fragment8;

import android.app.Activity;
import android.graphics.Color;
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.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainFragmentBottom extends Fragment {

 //
 public static String TAG = "MainFragmentBottom";

 private TextView mTextView;
 private Button mButtonView;
 
 // コールバック
 private FragmentBottomCallback mCallback;

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

  public void onClick(TextView v);
  public void onClick(Button button);

 }

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

 /*
  * 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 count = "";
  Bundle bundle = this.getArguments();
  
  if(bundle != null && bundle.containsKey("count")){
   count = getArguments().getString("count");
  }
  
  //
  Log.d("onCreateView", count);

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

 /*
  * TextViewの設定
  */
 public void setText(String text){
  mTextView.setText(text);
 }

}
■MainActivity.java

置換するときにアニメーションが指定できるそうです。
次の3つがあります。

・TRANSIT_FRAGMENT_OPEN
開く時のアニメーションを行うよう指定

・TRANSIT_FRAGMENT_CLOSE
閉じる時のアニメーションを行うよう指定

・TRANSIT_NONE
無し

これ以外にも、カスタムしたアニメーションを作成する事が可能らしいです。

その場合には、setCustomAnimationsを利用して設定します。
その際の注意点として、replaceの前にsetCustomAnimationsしないといけないそうです。
package com.example.fragment8;

import com.example.fragment8.MainFragmentBottom.FragmentBottomCallback;
import com.example.fragment8.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.Button;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements
  FragmentTopCallback, FragmentBottomCallback {

 public final static String TAG = "MainActivity";

 private int countCreate = 0;

 @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 fragmentTop = new MainFragmentTop();
   Bundle bundle1 = new Bundle();
   bundle1.putString("title", "キタ――(゚∀゚)――!!");
   // フラグメントに渡す値をセット
   fragmentTop.setArguments(bundle1);

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

 }

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

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

  // TextViewに設定されているタグを元にどっちがクリックされたか判定する
  String tag = (String) v.getTag();
  if (tag == MainFragmentTop.TAG) {
   //
   Log.d(TAG, "onClick " + String.valueOf(v.getTag()));

   //
   countCreate++;

   addFrament();

  } else if (tag == MainFragmentBottom.TAG) {
   //
   Log.d(TAG, "onClick " + String.valueOf(v.getTag()));

   // FragmentManager#popBackStackでフラグメントのバックスタックの状態を一つ戻す
   backFragment();

  }

 }

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.example.fragment7.MainFragmentBottom.FragmentBottomCallback#onClick
  * (android.widget.Button)
  */
 public void onClick(Button button) {
  // TextViewに設定されているタグを元にどっちがクリックされたか判定する
  String tag = (String) button.getTag();
  if (tag == MainFragmentTop.TAG) {
   replaceFragment();
  } else if (tag == MainFragmentBottom.TAG) {
   deleteBackStackFragment();
  }
 }

 /*
  * FrameLayoutにFragmentを追加する
  */
 public void addFrament() {

  Log.d(TAG, "addFrament");

  FragmentManager manager = getSupportFragmentManager();
  FragmentTransaction ft = manager.beginTransaction();

  MainFragmentBottom fragmentBottom = new MainFragmentBottom();
  Bundle bundle2 = new Bundle();
  bundle2.putString("count", String.valueOf(countCreate));
  // フラグメントに渡す値をセット
  fragmentBottom.setArguments(bundle2);

  // 生成するFragmentにつけるタグ名
  String tag = "fragment" + String.valueOf(countCreate);

  // Fragmentの作成
  ft.add(R.id.fragment_container, fragmentBottom, tag);

  // フラグメントをバックスタックに登録する。これを行うことで、後でこのフラグメントを参照したり、戻したりできる。
  ft.addToBackStack(null);

  // FragmentTransaction#commitでフラグメントのセットが完了する
  ft.commit();

 }

 /*
  * バックスタックの状態を1つ前に戻す。
  */
 public void backFragment() {

  //
  Log.d(TAG, "backFragment");

  FragmentManager manager = getSupportFragmentManager();
  // FragmentManager#popBackStackでフラグメントのバックスタックの状態を一つ戻す
  manager.popBackStack();

 }

 /*
  * バックスタックを全て削除する
  */
 public void deleteBackStackFragment() {

  //
  Log.d(TAG, "deleteBackStackFragment");

  FragmentManager manager = getSupportFragmentManager();
  // FragmentManager#popBackStackでフラグメントのバックスタックの状態を一つ戻す
  manager.popBackStack();

  if (manager.getBackStackEntryCount() > 0) {
   manager.popBackStack(manager.getBackStackEntryAt(0).getName(),
     FragmentManager.POP_BACK_STACK_INCLUSIVE);
  }
  // トランザクションを直ちに実行
  manager.executePendingTransactions();

 }

 /*
  * 置換する
  */
 public void replaceFragment() {

  Log.d(TAG, "replaceFragment");

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

  // 1つ目のfragmentを生成
  MainFragmentBottom fragmentBottom = new MainFragmentBottom();
  Bundle bundle2 = new Bundle();
  bundle2.putString("count", String.valueOf(countCreate));
  // フラグメントに渡す値をセット
  fragmentBottom.setArguments(bundle2);

  // Layout位置先の指定
  tx.replace(R.id.fragment_container, fragmentBottom);

  // Fragmentの変化時のアニメーションを指定
  tx.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

  //
  tx.addToBackStack(null);

  //
  tx.commit();

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

追加ボタンを押して2つFragmentを追加している状態にします。




この状態から、置換を押すと、一瞬下のFragmentが表示されます。

その後に置換されたFragmentが表示される。
(replaceは既存Fragmentを削除→追加の手順のため)

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

参考URL

0 件のコメント:

コメントを投稿