欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中的Fragment類使用進(jìn)階

 更新時間:2016年04月27日 09:41:50   作者:鴻洋_  
這篇文章主要介紹了Android中的Fragment類使用進(jìn)階,重點(diǎn)講解了Fragment與Activity的交互以及Fragment間的數(shù)據(jù)傳遞,需要的朋友可以參考下

0、回顧
Fragment 代表 Activity 當(dāng)中的一項(xiàng)操作或一部分用戶界面。 一個 Activity 中的多個 Fragment 可以組合在一起,形成一個多部分拼接而成的用戶界面組件,并可在多個 Activity 中復(fù)用。一個 Fragment 可被視為 Activity 中一個模塊化的部分, 它擁有自己的生命周期,并接收自己的輸入事件,在 Activity 運(yùn)行過程中可以隨時添加或移除它 (有點(diǎn)類似“子 Activity”,可在不同的 Activity 中重用)。

Fragment 必須嵌入某個 Activity 中,其生命周期直接受到宿主 Activity 生命周期的影響。 例如,當(dāng) Activity 被暫停(Paused)時,其內(nèi)部所有的 Fragment 也都會暫停。 而當(dāng) Activity 被銷毀時,它的 Fragment 也都會被銷毀。 不過,在 Activity 運(yùn)行期間(生命周期狀態(tài)處于 恢復(fù)(Resumed) 狀態(tài)時),每一個 Fragment 都可以被獨(dú)立地操作,比如添加或移除。 在執(zhí)行這些操作事務(wù)時,還可以將它們加入該 Activity 的回退棧(Back Stack)中 — Activity 回退棧的每個入口就是一條操作過的 Fragment 事務(wù)記錄。 回退堆棧使得用戶可以通過按下 回退(Back) 鍵來回退 Fragment 事務(wù)(后退一步)。

當(dāng)把 Fragment 加入 Activity 布局(Layout) 后,它位于 Activity View 層次架構(gòu)(Hierarchy)的某個 ViewGroup 里,且擁有自己的 View 布局定義。 通過在 Activity 的 Layout 文件中聲明 <fragment> 元素,可以在 Layout 中添加一個 Fragment。 也可以用程序代碼在已有的 ViewGroup 中添加一個 Fragment。 不過, Fragment 并不一定非要是 Activity 布局的一部分,它也可以沒有自己的界面,而是用作 Activity 的非可視化工作組件。


1、概述
相信大家對Fragment的都不陌生,對于Fragment的使用,一方面Activity需要在布局中為Fragment安排位置,另一方面需要管理好Fragment的生命周期。Activity中有個FragmentManager,其內(nèi)部維護(hù)fragment隊(duì)列,以及fragment事務(wù)的回退棧。
一般情況下,我們在Activity里面會這么添加Fragment:

public class MainActivity extends FragmentActivity 
{ 
   
  private ContentFragment mContentFragment ;  
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
   
    FragmentManager fm = getSupportFragmentManager(); 
    mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container); 
     
    if(mContentFragment == null ) 
    { 
      mContentFragment = new ContentFragment(); 
      fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit(); 
    } 
 
  } 
 
} 

針對上面代碼,問兩個問題:
(1)為什么需要判null呢?
主要是因?yàn)?,?dāng)Activity因?yàn)榕渲冒l(fā)生改變(屏幕旋轉(zhuǎn))或者內(nèi)存不足被系統(tǒng)殺死,造成重新創(chuàng)建時,我們的fragment會被保存下來,但是會創(chuàng)建新的FragmentManager,新的FragmentManager會首先會去獲取保存下來的fragment隊(duì)列,重建fragment隊(duì)列,從而恢復(fù)之前的狀態(tài)。
(2)add(R.id.id_fragment_container,mContentFragment)中的布局的id有何作用?
一方面呢,是告知FragmentManager,此fragment的位置;另一方面是此fragment的唯一標(biāo)識;就像我們上面通過fm.findFragmentById(R.id.id_fragment_container)查找~~
好了,簡單回顧了一下基本用法,具體的還請參考上面的博客或者其他資料,接下來,介紹一些使用的意見~~

2、Fragment Arguments
下面描述一個簡單的場景,比如我們某個按鈕觸發(fā)Activity跳轉(zhuǎn),需要通過Intent傳遞參數(shù)到目標(biāo)Activity的Fragment中,那么此Fragment如何獲取當(dāng)前的Intent的值呢?
有哥們會說,這個簡單?看我的代碼(問題代碼):

public class ContentFragment extends Fragment 
{ 
   
  private String mArgument ;  
  public static final String ARGUMENT ="argument"; 
  @Override 
  public void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
     
    mArgument = getActivity().getIntent().getStringExtra(ARGUMENT); 
     
  } 

我們直接在Fragment的onCreate中,拿到宿主Activty,宿主Activity中肯定能通過getIntent拿到Intent,然后通過get方法,隨意拿參數(shù)~~
這么寫,功能上是實(shí)現(xiàn)了,但是呢?存在一個大問題:我們用Fragment的一個很大的原因,就是為了復(fù)用。你這么寫,相當(dāng)于這個Fragment已經(jīng)完全和當(dāng)前這個宿主Activity綁定了,復(fù)用直接廢了~~~所以呢?我們換種方式,推薦使用arguments來創(chuàng)建Fragment。

public class ContentFragment extends Fragment 
{ 
 
  private String mArgument; 
  public static final String ARGUMENT = "argument"; 
 
  @Override 
  public void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    // mArgument = getActivity().getIntent().getStringExtra(ARGUMENT); 
    Bundle bundle = getArguments(); 
    if (bundle != null) 
      mArgument = bundle.getString(ARGUMENT); 
 
  } 
 
  /** 
   * 傳入需要的參數(shù),設(shè)置給arguments 
   * @param argument 
   * @return 
   */ 
  public static ContentFragment newInstance(String argument) 
  { 
    Bundle bundle = new Bundle(); 
    bundle.putString(ARGUMENT, argument); 
    ContentFragment contentFragment = new ContentFragment(); 
    contentFragment.setArguments(bundle); 
    return contentFragment; 
  } 

給Fragment添加newInstance方法,將需要的參數(shù)傳入,設(shè)置到bundle中,然后setArguments(bundle),最后在onCreate中進(jìn)行獲??;
這樣就完成了Fragment和Activity間的解耦。當(dāng)然了這里需要注意:
setArguments方法必須在fragment創(chuàng)建以后,添加給Activity前完成。千萬不要,首先調(diào)用了add,然后設(shè)置arguments。

3、Fragment的startActivityForResult
依舊是一個簡單的場景:兩個Fragment,一個展示文章列表的Fragment(叫做ListTitleFragment),一個顯示詳細(xì)信息的Fragment(叫做:ContentFragment),當(dāng)然了,這兩個Fragment都有其宿主Activity。
現(xiàn)在,我們點(diǎn)擊列表Fragment中的列表項(xiàng),傳入相應(yīng)的參數(shù),去詳細(xì)信息的Fragment展示詳細(xì)的信息,在詳細(xì)信息頁面,用戶可以進(jìn)行點(diǎn)評,當(dāng)用戶點(diǎn)擊back以后,我們以往點(diǎn)評結(jié)果顯示在列表的Fragment對于的列表項(xiàng)中;
也就是說,我們點(diǎn)擊跳轉(zhuǎn)到對應(yīng)Activity的Fragment中,并且希望它能夠返回參數(shù),那么我們肯定是使用Fragment.startActivityForResult ;
在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,沒有setResult()方法,用于設(shè)置返回的intent,這樣我們就需要通過調(diào)用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);。
詳細(xì)代碼:
ListTitleFragment

public class ListTitleFragment extends ListFragment 
{ 
 
  public static final int REQUEST_DETAIL = 0x110; 
  private List<String> mTitles = Arrays.asList("Hello", "World", "Android"); 
  private int mCurrentPos ;  
  private ArrayAdapter<String> mAdapter ;  
 
   
  @Override 
  public void onActivityCreated(Bundle savedInstanceState) 
  { 
    super.onActivityCreated(savedInstanceState); 
    setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles)); 
  } 
   
  @Override 
  public void onListItemClick(ListView l, View v, int position, long id) 
  { 
    mCurrentPos = position ;  
    Intent intent = new Intent(getActivity(),ContentActivity.class); 
    intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position)); 
    startActivityForResult(intent, REQUEST_DETAIL); 
  } 
 
   
  @Override 
  public void onActivityResult(int requestCode, int resultCode, Intent data) 
  { 
    Log.e("TAG", "onActivityResult"); 
    super.onActivityResult(requestCode, resultCode, data); 
    if(requestCode == REQUEST_DETAIL) 
    { 
      mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- "+data.getStringExtra(ContentFragment.RESPONSE)); 
      mAdapter.notifyDataSetChanged(); 
    } 
  } 
} 

ContentFragment

public class ContentFragment extends Fragment 
{ 
 
  private String mArgument; 
  public static final String ARGUMENT = "argument"; 
  public static final String RESPONSE = "response"; 
 
  @Override 
  public void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    Bundle bundle = getArguments(); 
    if (bundle != null) 
    { 
      mArgument = bundle.getString(ARGUMENT); 
      Intent intent = new Intent(); 
      intent.putExtra(RESPONSE, "good"); 
      getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent); 
    } 
 
  } 
 
  public static ContentFragment newInstance(String argument) 
  { 
    Bundle bundle = new Bundle(); 
    bundle.putString(ARGUMENT, argument); 
    ContentFragment contentFragment = new ContentFragment(); 
    contentFragment.setArguments(bundle); 
    return contentFragment; 
  } 
 
  @Override 
  public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) 
  { 
    Random random = new Random(); 
    TextView tv = new TextView(getActivity()); 
    tv.setText(mArgument); 
    tv.setGravity(Gravity.CENTER); 
    tv.setBackgroundColor(Color.argb(random.nextInt(100), 
        random.nextInt(255), random.nextInt(255), random.nextInt(255))); 
    return tv; 
  } 
} 

貼出了兩個Fragment的代碼,可以看到我們在ListTitleFragment.onListItemClick,使用startActivityForResult()跳轉(zhuǎn)到目標(biāo)Activity,在目標(biāo)Activity的Fragment(ContentFragment)中獲取參數(shù),然后調(diào)用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);進(jìn)行設(shè)置返回的數(shù)據(jù);最后在ListTitleFragment.onActivityResult()拿到返回的數(shù)據(jù)進(jìn)行回顯;
為大家以后在遇到類似問題時,提供了解決方案;也說明了一個問題:fragment能夠從Activity中接收返回結(jié)果,但是其自設(shè)無法產(chǎn)生返回結(jié)果,只有Activity擁有返回結(jié)果。
接下來我要貼一下,這兩個Fragment的宿主Activity:
ListTitleActivity

public class ListTitleActivity extends FragmentActivity 
{ 
 
  private ListTitleFragment mListFragment; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_single_fragment); 
   
    FragmentManager fm = getSupportFragmentManager(); 
    mListFragment = (ListTitleFragment) fm.findFragmentById(R.id.id_fragment_container); 
     
    if(mListFragment == null ) 
    { 
      mListFragment = new ListTitleFragment(); 
      fm.beginTransaction().add(R.id.id_fragment_container,mListFragment).commit(); 
    } 
 
  } 
} 


ContentActivity:

public class ContentActivity extends FragmentActivity 
{ 
 
  private ContentFragment mContentFragment; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) 
  { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_single_fragment); 
   
    FragmentManager fm = getSupportFragmentManager(); 
    mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container); 
     
    if(mContentFragment == null ) 
    { 
      String title = getIntent().getStringExtra(ContentFragment.ARGUMENT); 
      mContentFragment = ContentFragment.newInstance(title); 
      fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit(); 
    } 
 
  } 
} 

有沒有發(fā)現(xiàn)兩個Activity中的代碼極其的類似,且使用了同一個布局文件:
activity_single_fragment.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:id="@+id/id_fragment_container" 
> 
 
 
</RelativeLayout> 

為什么要貼這Acticity的代碼呢?因?yàn)槲覀冺?xiàng)目中,如果原則上使用Fragment,會發(fā)現(xiàn)大量類似的代碼,那么我們就可以抽象一個Activity出來,托管我們的Single Fragment。

4、SingleFragmentActivity
于是抽象出來的Activity的代碼為:

package com.example.demo_zhy_23_fragments; 
 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentManager; 
 
public abstract class SingleFragmentActivity extends FragmentActivity 
{ 
  protected abstract Fragment createFragment(); 
   
  @Override 
  protected void onCreate(Bundle savedInstanceState) 
  { 
 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_single_fragment); 
   
    FragmentManager fm = getSupportFragmentManager(); 
    Fragment fragment =fm.findFragmentById(R.id.id_fragment_container); 
     
    if(fragment == null ) 
    { 
      fragment = createFragment() ; 
       
      fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit(); 
    } 
  } 
   
} 

那么,有了這個SingleFragmentActivity,我們的ContentActivity和ListTitleActivity也能大變身了~

package com.example.demo_zhy_23_fragments; 
 
import android.support.v4.app.Fragment; 
 
public class ContentActivity extends SingleFragmentActivity 
{ 
  private ContentFragment mContentFragment; 
 
  @Override 
  protected Fragment createFragment() 
  { 
    String title = getIntent().getStringExtra(ContentFragment.ARGUMENT); 
 
    mContentFragment = ContentFragment.newInstance(title); 
    return mContentFragment; 
  } 
} 

package com.example.demo_zhy_23_fragments; 
 
import android.support.v4.app.Fragment; 
 
public class ListTitleActivity extends SingleFragmentActivity 
{ 
  private ListTitleFragment mListFragment; 
 
  @Override 
  protected Fragment createFragment() 
  { 
    mListFragment = new ListTitleFragment(); 
    return mListFragment; 
  } 
} 

是不是簡潔很多,相信優(yōu)先使用Fragment的項(xiàng)目,類似的Activity非常多,使用SingleFragmentActivity來簡化你的代碼吧~~
好了,此代碼是來自文章開始推薦的書中的,再次推薦一下~~。

5、FragmentPagerAdapter與FragmentStatePagerAdapter
相信這兩個PagerAdapter的子類,大家都不陌生吧~~自從Fragment問世,使用ViewPager再結(jié)合上面任何一個實(shí)例的制作APP主頁的案例特別多~~~
那么這兩個類有何區(qū)別呢?
主要區(qū)別就在與對于fragment是否銷毀,下面細(xì)說:
(1)FragmentPagerAdapter:對于不再需要的fragment,選擇調(diào)用detach方法,僅銷毀視圖,并不會銷毀fragment實(shí)例。
(2)FragmentStatePagerAdapter:會銷毀不再需要的fragment,當(dāng)當(dāng)前事務(wù)提交以后,會徹底的將fragmeng從當(dāng)前Activity的FragmentManager中移除,state標(biāo)明,銷毀時,會將其onSaveInstanceState(Bundle outState)中的bundle信息保存下來,當(dāng)用戶切換回來,可以通過該bundle恢復(fù)生成新的fragment,也就是說,你可以在onSaveInstanceState(Bundle outState)方法中保存一些數(shù)據(jù),在onCreate中進(jìn)行恢復(fù)創(chuàng)建。
如上所說,使用FragmentStatePagerAdapter當(dāng)然更省內(nèi)存,但是銷毀新建也是需要時間的。一般情況下,如果你是制作主頁面,就3、4個Tab,那么可以選擇使用FragmentPagerAdapter,如果你是用于ViewPager展示數(shù)量特別多的條目時,那么建議使用FragmentStatePagerAdapter。
篇幅原因,具體的案例就不寫了,大家自行測試。

6、Fragment間的數(shù)據(jù)傳遞
上面3中,我們展示了,一般的兩個Fragment間的數(shù)據(jù)傳遞。
那么還有一種比較特殊的情況,就是兩個Fragment在同一個Activity中:例如,點(diǎn)擊當(dāng)前Fragment中按鈕,彈出一個對話框(DialogFragment),在對話框中的操作需要返回給觸發(fā)的Fragment中,那么如何數(shù)據(jù)傳遞呢?對于對話框的使用推薦:Android 官方推薦 : DialogFragment 創(chuàng)建對話框
我們繼續(xù)修改我們的代碼:現(xiàn)在是ListTitleFragment , ContentFragment , 添加一個對話框:EvaluateDialog,用戶點(diǎn)擊ContentFragment 內(nèi)容時彈出一個評價列表,用戶選擇評價。
現(xiàn)在我們的關(guān)注點(diǎn)在于:ContentFragment中如何優(yōu)雅的拿到EvaluateDialog中返回的評價:
記住我們在一個Activity中,那么肯定不是使用startActivityForResult;但是我們返回的數(shù)據(jù),依然在onActivityResult中進(jìn)行接收。
好了看代碼:
ContentFragment 

public class ContentFragment extends Fragment 
{ 
 
  private String mArgument; 
  public static final String ARGUMENT = "argument"; 
  public static final String RESPONSE = "response"; 
  public static final String EVALUATE_DIALOG = "evaluate_dialog"; 
  public static final int REQUEST_EVALUATE = 0X110; 
 
  //... 
 
  @Override 
  public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) 
  { 
    Random random = new Random(); 
    TextView tv = new TextView(getActivity()); 
    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 
        LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 
    tv.setLayoutParams(params); 
    tv.setText(mArgument); 
    tv.setGravity(Gravity.CENTER); 
    tv.setBackgroundColor(Color.argb(random.nextInt(100), 
        random.nextInt(255), random.nextInt(255), random.nextInt(255))); 
    // set click 
    tv.setOnClickListener(new OnClickListener() 
    { 
 
      @Override 
      public void onClick(View v) 
      { 
        EvaluateDialog dialog = new EvaluateDialog(); 
        //注意setTargetFragment 
        dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE); 
        dialog.show(getFragmentManager(), EVALUATE_DIALOG); 
      } 
    }); 
    return tv; 
  } 
 
  //接收返回回來的數(shù)據(jù) 
  @Override 
  public void onActivityResult(int requestCode, int resultCode, Intent data) 
  { 
    super.onActivityResult(requestCode, resultCode, data); 
 
    if (requestCode == REQUEST_EVALUATE) 
    { 
      String evaluate = data 
          .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE); 
      Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show(); 
      Intent intent = new Intent(); 
      intent.putExtra(RESPONSE, evaluate); 
      getActivity().setResult(Activity.REQUEST_OK, intent); 
    } 
 
  } 
} 

刪除了一些無關(guān)代碼,注意看,我們在onCreateView中為textview添加了click事件,用于彈出我們的dialog,注意一行代碼:
dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
我們調(diào)用了Fragment.setTargetFragment ,這個方法,一般就是用于當(dāng)前fragment由別的fragment啟動,在完成操作后返回?cái)?shù)據(jù)的,符合我們的需求吧~~~注意,這句很重要。
接下來看EvaluateDialog代碼:

package com.example.demo_zhy_23_fragments; 
 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.app.Dialog; 
import android.content.DialogInterface; 
import android.content.DialogInterface.OnClickListener; 
import android.content.Intent; 
import android.os.Bundle; 
import android.support.v4.app.DialogFragment; 
 
public class EvaluateDialog extends DialogFragment 
{ 
  private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" }; 
  public static final String RESPONSE_EVALUATE = "response_evaluate"; 
 
  @Override 
  public Dialog onCreateDialog(Bundle savedInstanceState) 
  { 
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 
 
    builder.setTitle("Evaluate :").setItems(mEvaluteVals, 
        new OnClickListener() 
        { 
          @Override 
          public void onClick(DialogInterface dialog, int which) 
          { 
            setResult(which); 
          } 
        }); 
    return builder.create(); 
  } 
 
  // 設(shè)置返回?cái)?shù)據(jù) 
  protected void setResult(int which) 
  { 
    // 判斷是否設(shè)置了targetFragment 
    if (getTargetFragment() == null) 
      return; 
 
    Intent intent = new Intent(); 
    intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]); 
    getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE, 
        Activity.RESULT_OK, intent); 
 
  } 
} 

重點(diǎn)就是看點(diǎn)擊后的setResult了,我們首先判斷是否設(shè)置了targetFragment,如果設(shè)置了,意味我們要返回一些數(shù)據(jù)到targetFragment。
我們創(chuàng)建intent封裝好需要傳遞數(shù)據(jù),最后手動調(diào)用onActivityResult進(jìn)行返回?cái)?shù)據(jù)~~
最后我們在ContentFragment的onActivityResult接收即可。

ok,終于把這些tips貫穿到一起了,到此我們的Fragment的一些建議的用法就結(jié)束了~~~那么,最后提供下源碼,也順便貼個效果圖:

相關(guān)文章

最新評論