前言
fragment自動3.0推出以后,在碎片化處理方面一直很受歡迎,平時在項目中也在頻繁使用,自從看了CSDN上郭大神和鴻洋大神對fragment的詳細總結后,自己學習做個筆記。
郭大神:http://blog.csdn.net/guolin_blog/article/details/8881711
鴻洋大神:http://blog.csdn.net/lmj623565791/article/details/42628537
fragment的常用方法
(1) 靜態使用
在布局文件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去實現的。
(2)動態使用
動態使用分為四個步驟
(a)獲取FragmentManager,通過方法getSupportFragmentManager()獲取。
(b)開啟一個事物,通過beginTransaction方法開啟。
(c)向容器內加入Fragment,默認第一個是通過add()方法添加,后面再替換時通過replace方法實現,需要傳入容器的id和Fragment的實例。
(d)提交事務,調用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數據為空");
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數據為"+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.在設置默認的fragment時,不能調用addToBackStack(null)方法,如果添加了在退出應用時會在Activity頁面出現一個空白頁面。
b.在代碼中有兩個Fragment,分別為FrangmentOne和FragmentTwo,FragmentOne為默認的,從FragmentOne中的Button 去啟動FragmentTwo,在鴻洋大神的分析中當我們從FragmentOne啟動到FragmentTwo時使用的是replace方法,replace方法的實現過程是remove方法和add方法的合體,當我們在調用該方法時,如果不添加事務到回退棧,前一個Fragment實例會被銷毀,調用了addToBackStack(null);將當前的事務添加到了回退棧,所以FragmentOne實例不會被銷毀,但是視圖層次依然會被銷毀,即會調用onDestoryView和onCreateView,在這種情況下,再次返回到FragmentOne時等于新建了一個FragmentOne實例對象,在之前Fragment中保存的數據將不會存在,而在真實的案例中當我們從FragmentTwo返回到FragmentOne時希望數據還在,因此此時就不能使用replace方法,使用hide方法將FragmentOne隱藏起來,當FragmengTwo返回時會調用show方法顯示,相應的數據也會被保存。
c.當fragment遇到屏幕自動旋轉時,會導致Activity和Fragment的重建在這種情況下可能會導致兩個FragmentOne的實例對象存在,界面上也會存在重疊效果,導致這種現象的原因是當Activity重建時我們的fragment會被保存下來,但是會創建新的FragmentManager,新的FragmentManager會首先會去獲取保存下來的fragment隊列,重建fragment隊列,從而恢復之前的狀態。解決方法是在創建實例和獲取FragmentManager時判斷saveInstanceState狀態,當我們第一次啟動Activity時,saveInstanceState為空的,當因異常情況導致Activity重建時,自動會調用saveInstanceState會保存一些信息,因此通過判斷saveInstanceState狀態可以避免這種情況。
d.在參考了大神的博客后自己嘗試技能滿足保存數據又可以避免在屏幕旋轉后避免創建多個Fragment實例對象,實現方法為創建實例前對saveInstanceState狀態進行判斷,同時在啟動其他的Fragment時不調用replace方法使用hide方法,但是出現nullPointerException,目前還沒解決。
(3)Activity與Fragment以及Fragment與Fragment之間的通信。
a.Activity與Fragment
實現方式有以下幾種
①.handler通信
②.接口回調 例如上面代碼中在FragmentTwo 實現方式,在
FragmentTwo中的點擊事件通過接口回調交給Activity去處理。
聲明接口
public interface FOneListener{
void onOneClick();
}
點擊事件傳遞
public void onClick(View v) {
if(getActivity() instanceof FOneListener){
((FOneListener)getActivity()).onOneClick();
}
}
父類繼承去實現
public class MainActivity extends AppCompatActivity implements FragementOne.FOneListener
@Override
public void onOneClick() {
//處理點擊事件
}
③.使用EventBus去實現。
④.使用廣播BroadcastReceiver去實現。
b.同時存在同一個Activity中的頁面中的兩個Fragment之間的通信
在查看郭大神的博客后發現這種情況適用于存在于同一個頁面之間FragmentA 去獲取FragmentB(或者FragmentB 去獲取FragmentA)中某個組件的信息。主要都是通過getActivity這個方法實現的,getActivity方法可以讓Fragment獲取到關聯的Activity,然后再調用Activity的findViewById方法,就可以獲取到和這個Activity關聯的其它Fragment的視圖,從而可以獲取這個視圖的數據。
@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的復用以及與Activity之間的解耦
在我們項目想法中肯定會經常遇到ViewPager與Fragment之間的結合使用,使用Viewpager來切換不同的fragment,當第一次使用時可能會針對每一個主題都會創建一個Fragment去實現,這種實現方式在網上有很多案例,但是這種實現方式創建了很多的fragment,代碼有太多的耦合性,自從Google官網給出了Tablayout這個組件時,它也是結合fragmen和viewpager去實現的。在這個具體實現時很好的復用了fragment。具體實現見代碼吧,口頭敘述態度都是扯淡,只有代碼才是王道。
布局代碼(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方法,將需要的參數傳入,設置到bundle中,然后setArguments(bundle),最后在onCreate中進行獲取通過getArguments()去獲取一個fragment的實例,通過這種方式可以實現Fragment的復用。