前言
fragment自動3.0推出以后再榄,在碎片化處理方面一直很受歡迎蝙叛,平時在項目中也在頻繁使用,自從看了CSDN上郭大神和鴻洋大神對fragment的詳細總結(jié)后绞惦,自己學(xué)習做個筆記逼纸。
郭大神:http://blog.csdn.net/guolin_blog/article/details/8881711
鴻洋大神:http://blog.csdn.net/lmj623565791/article/details/42628537
fragment的常用方法
(1) 靜態(tài)使用
在布局文件xml中使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<fragment
android:id="@+id/fragmentOne"
android:name="com.ydscience.FragmentOne"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragmentTwo"
android:name="com.ydscience.FragmentTwo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
具體的fragmentOne和fragmentTwo的代碼就不粘貼啦,都是繼承Fragment去實現(xiàn)的济蝉。
(2)動態(tài)使用
動態(tài)使用分為四個步驟
(a)獲取FragmentManager,通過方法getSupportFragmentManager()獲取杰刽。
(b)開啟一個事物,通過beginTransaction方法開啟王滤。
(c)向容器內(nèi)加入Fragment贺嫂,默認第一個是通過add()方法添加,后面再替換時通過replace方法實現(xiàn)雁乡,需要傳入容器的id和Fragment的實例第喳。
(d)提交事務(wù),調(diào)用commit方法提交踱稍。
xml中的代碼為
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ydsciernce.studytest.MainActivity">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/content_fragment"/>
</RelativeLayout>
Activity中的代碼為
public class MainActivity extends AppCompatActivity implements FragementOne.FOneListener{
FragmentTwo mFragmentTwo;
FragementOne mFragmentOne;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null){
Log.i("TAG","saveInstanece數(shù)據(jù)為空");
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
mFragmentOne = new FragementOne();
transaction.add(R.id.content_fragment,mFragmentOne,"ONE");
transaction.commit();
}else {
Log.i("TAG","saveInstanece數(shù)據(jù)為"+savedInstanceState.toString());
}
}
@Override
public void onOneClick() {
if (mFragmentTwo == null){
mFragmentTwo = new FragmentTwo();
}
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
//transaction.hide(mFragmentOne);
//transaction.add(R.id.content_fragment,mFragmentTwo,"TWO");
transaction.replace(R.id.content_fragment,mFragmentTwo,"TWO");
transaction.addToBackStack(null);
transaction.commit();
}
}
FragmentOne的代碼為
public class FragementOne extends Fragment implements View.OnClickListener{
private Button mButton;
EditText test;
public interface FOneListener{
void onOneClick();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
Log.i("TAG"," on createview");
View view = inflater.inflate(R.layout.content_one_fragment,null);
mButton = (Button) view.findViewById(R.id.buttonOne);
mButton.setOnClickListener(this);
test = (EditText) view.findViewById(R.id.editText);
Log.i("TAG","edit datda"+test.getText().toString());
return view;
}
@Override
public void onClick(View v) {
if(getActivity() instanceof FOneListener){
((FOneListener)getActivity()).onOneClick();
}
}
}
注意事項:
a.在設(shè)置默認的fragment時曲饱,不能調(diào)用addToBackStack(null)方法,如果添加了在退出應(yīng)用時會在Activity頁面出現(xiàn)一個空白頁面珠月。
b.在代碼中有兩個Fragment渔工,分別為FrangmentOne和FragmentTwo,FragmentOne為默認的,從FragmentOne中的Button 去啟動FragmentTwo,在鴻洋大神的分析中當我們從FragmentOne啟動到FragmentTwo時使用的是replace方法桥温,replace方法的實現(xiàn)過程是remove方法和add方法的合體,當我們在調(diào)用該方法時梁丘,如果不添加事務(wù)到回退棧侵浸,前一個Fragment實例會被銷毀,調(diào)用了addToBackStack(null);將當前的事務(wù)添加到了回退棧氛谜,所以FragmentOne實例不會被銷毀掏觉,但是視圖層次依然會被銷毀,即會調(diào)用onDestoryView和onCreateView值漫,在這種情況下澳腹,再次返回到FragmentOne時等于新建了一個FragmentOne實例對象,在之前Fragment中保存的數(shù)據(jù)將不會存在,而在真實的案例中當我們從FragmentTwo返回到FragmentOne時希望數(shù)據(jù)還在酱塔,因此此時就不能使用replace方法沥邻,使用hide方法將FragmentOne隱藏起來,當FragmengTwo返回時會調(diào)用show方法顯示羊娃,相應(yīng)的數(shù)據(jù)也會被保存唐全。
c.當fragment遇到屏幕自動旋轉(zhuǎn)時,會導(dǎo)致Activity和Fragment的重建在這種情況下可能會導(dǎo)致兩個FragmentOne的實例對象存在蕊玷,界面上也會存在重疊效果邮利,導(dǎo)致這種現(xiàn)象的原因是當Activity重建時我們的fragment會被保存下來,但是會創(chuàng)建新的FragmentManager垃帅,新的FragmentManager會首先會去獲取保存下來的fragment隊列延届,重建fragment隊列,從而恢復(fù)之前的狀態(tài)贸诚。解決方法是在創(chuàng)建實例和獲取FragmentManager時判斷saveInstanceState狀態(tài)方庭,當我們第一次啟動Activity時,saveInstanceState為空的赦颇,當因異常情況導(dǎo)致Activity重建時二鳄,自動會調(diào)用saveInstanceState會保存一些信息,因此通過判斷saveInstanceState狀態(tài)可以避免這種情況媒怯。
d.在參考了大神的博客后自己嘗試技能滿足保存數(shù)據(jù)又可以避免在屏幕旋轉(zhuǎn)后避免創(chuàng)建多個Fragment實例對象订讼,實現(xiàn)方法為創(chuàng)建實例前對saveInstanceState狀態(tài)進行判斷,同時在啟動其他的Fragment時不調(diào)用replace方法使用hide方法扇苞,但是出現(xiàn)nullPointerException,目前還沒解決欺殿。
(3)Activity與Fragment以及Fragment與Fragment之間的通信。
a.Activity與Fragment
實現(xiàn)方式有以下幾種
”罘蟆①.handler通信
〔彼铡②.接口回調(diào) 例如上面代碼中在FragmentTwo 實現(xiàn)方式,在
FragmentTwo中的點擊事件通過接口回調(diào)交給Activity去處理定踱。
聲明接口
public interface FOneListener{
void onOneClick();
}
點擊事件傳遞
public void onClick(View v) {
if(getActivity() instanceof FOneListener){
((FOneListener)getActivity()).onOneClick();
}
}
父類繼承去實現(xiàn)
public class MainActivity extends AppCompatActivity implements FragementOne.FOneListener
@Override
public void onOneClick() {
//處理點擊事件
}
③.使用EventBus去實現(xiàn)棍潘。
④.使用廣播BroadcastReceiver去實現(xiàn)崖媚。
b.同時存在同一個Activity中的頁面中的兩個Fragment之間的通信
在查看郭大神的博客后發(fā)現(xiàn)這種情況適用于存在于同一個頁面之間FragmentA 去獲取FragmentB(或者FragmentB 去獲取FragmentA)中某個組件的信息亦歉。主要都是通過getActivity這個方法實現(xiàn)的,getActivity方法可以讓Fragment獲取到關(guān)聯(lián)的Activity畅哑,然后再調(diào)用Activity的findViewById方法肴楷,就可以獲取到和這個Activity關(guān)聯(lián)的其它Fragment的視圖,從而可以獲取這個視圖的數(shù)據(jù)荠呐。
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = (Button) getActivity().findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TextView textView = (TextView) getActivity().findViewById(R.id.fragment1_text);
Toast.makeText(getActivity(), textView.getText(), Toast.LENGTH_LONG).show();
}
});
}
(4)Fragment的復(fù)用以及與Activity之間的解耦
在我們項目想法中肯定會經(jīng)常遇到ViewPager與Fragment之間的結(jié)合使用赛蔫,使用Viewpager來切換不同的fragment,當?shù)谝淮问褂脮r可能會針對每一個主題都會創(chuàng)建一個Fragment去實現(xiàn)砂客,這種實現(xiàn)方式在網(wǎng)上有很多案例,但是這種實現(xiàn)方式創(chuàng)建了很多的fragment呵恢,代碼有太多的耦合性鞠值,自從Google官網(wǎng)給出了Tablayout這個組件時,它也是結(jié)合fragmen和viewpager去實現(xiàn)的瑰剃。在這個具體實現(xiàn)時很好的復(fù)用了fragment齿诉。具體實現(xiàn)見代碼吧,口頭敘述態(tài)度都是扯淡晌姚,只有代碼才是王道粤剧。
布局代碼(content_history_record.xml)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tabLayout"
app:tabBackground="@color/historyColorAccent"
app:tabSelectedTextColor="@color/tabSelected"
app:tabTextColor="@color/tabUnSelected"
app:tabMode="scrollable"/>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/label"
android:id="@+id/viewpager_history"
/>
</LinearLayout>
MainActivity的代碼
public class HistoryRecordActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_history_record);
init();
}
private void init(){
HistoryFragmentPreferenceAdapter adapter = new HistoryFragmentPreferenceAdapter(getSupportFragmentManager(),this);
ViewPager historyPager = (ViewPager) findViewById(R.id.viewpager_history);
historyPager.setAdapter(adapter);
TabLayout historyTabLayout = (TabLayout) findViewById(R.id.tabLayout);
historyTabLayout.setupWithViewPager(historyPager);
historyTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
historyTabLayout.setTabMode(TabLayout.MODE_FIXED);
}
}
適配器的代碼(HistoryFragmentPreferenceAdapter.java)
public class HistoryFragmentPreferenceAdapter extends FragmentPagerAdapter {
private Context mContext;
private final int coutnt = 5;
private String [] titles ;
public HistoryFragmentPreferenceAdapter(FragmentManager fm, Context context) {
super(fm);
this.mContext = context;
titles = context.getResources().getStringArray(R.array.history_title);
}
@Override
public Fragment getItem(int position) {
return PageFragment.getIntance(position+1);
}
@Override
public int getCount() {
return coutnt;
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
}
Fragment的代碼(PageFragment.java)
public class PageFragment extends Fragment {
public static final String TRANSFER_PAGE = "page";
private int mPage;
public static Fragment getIntance(int page){
Bundle bundle = new Bundle();
bundle.putInt(TRANSFER_PAGE,page);
PageFragment pageFragment = new PageFragment();
pageFragment.setArguments(bundle);
return pageFragment;
}
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
mArgument = bundle.getString(TRANSFER_PAGE);
}
給Fragment添加newInstance方法,將需要的參數(shù)傳入挥唠,設(shè)置到bundle中抵恋,然后setArguments(bundle),最后在onCreate中進行獲取通過getArguments()去獲取一個fragment的實例宝磨,通過這種方式可以實現(xiàn)Fragment的復(fù)用弧关。