前言
fragment在項目中廣泛使用,但是怎么使用才能發揮它的最大價值呢,我認為有個標準就是:你的fragment是否能復用,隨便扔到哪個Acvtivity都玩得轉。從實際開發中我總結了以下幾點,旨在更規范地使用fragment(當然不是所有fragment都必須來復用,也可以專門處理某些業務,那就沒必要刻意追求fragment的規范了,隨意用就好。。)
Fragment or Activity?
我的看法是不要去比較兩者的性能,沒有意義,google讓他們都存在肯定都是有價值的。它們的應用場景不同。Activity更傾向于一個整體模塊容器,而Fragment是其中的子模塊。可以理解成一個工廠(App)有N個生產不同產品的產房(Activity),每個廠房(Activity)里面有生產N類子產品的機器(Fragment)。所以,Activity的存在可以對應用更好的結構化和模塊化的劃分,讓應用有更健壯和清晰的層次,而Fragment可以讓將應用的功能細化和具象化。兩者沒有好壞之分,根據功能劃分粒度來選取合適的載體才是正確的架構方式。
基本使用
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呢?
主要是因為,當Activity因為配置發生改變(屏幕旋轉)或者內存不足被系統殺死,造成重新創建時,我們的fragment會被保存下來,但是會創建新的FragmentManager,新的FragmentManager會首先會去獲取保存下來的fragment隊列,重建fragment隊列,從而恢復之前的狀態。
2、add(R.id.id_fragment_container,mContentFragment)中的布局的id有何作用?
一方面呢,是告知FragmentManager,此fragment的位置;另一方面是此fragment的唯一標識;就像我們上面通過fm.findFragmentById(R.id.id_fragment_container)查找
封裝BaseFragment基類
public abstract class BaseFragment extends Fragment {
protected View mRootView;@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(null == mRootView){
mRootView = inflater.inflate(getLayoutId(), container, false);
}
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
afterCreate(savedInstanceState);
}
//實現fragment的布局
protected abstract int getLayoutId();
protected abstract void afterCreate(Bundle savedInstanceState);
}
使用靜態工廠方法newInstance(...)來獲取Fragment實例
為啥要這么弄?fragment應用到不同的Activity,我們可以重載newInstance方法,根據需求new不同的fragment實例
public class WeatherFragment extends Fragment{
private static final String ARG1 = "arg1";
public static WeatherFragment newInstance(String cityName) {
Bundle args = new Bundle();
args.putString(cityName,ARG1);
WeatherFragment fragment = new WeatherFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
String city = bundle.getString(ARG1);
}
}
}
fragment監聽虛擬按鍵和back按鍵
//我見過很多方法,這個方法是最好的,給rootView設置一個OnKeyListener來監聽key事件
mRootView.setFocusable(true);
mRootView.setFocusableInTouchMode(true);
mRootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//不一定是要觸發返回棧,可以做一些其他的事情,我只是舉個栗子。
getActivity().onBackPressed();
return true;
}
return false;
}
});
回退棧
如果一個Activity有FragmentOne打開了FragmentTwo,想要back回退,可以使用回退棧
http://img.blog.csdn.net/20140720144355734
FragmentThree fThree = new FragmentThree();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
tx.add(R.id.id_content , fThree, "THREE"); //如果是hide,add那么fragment的視圖不會重繪,比如edittext輸入文字還會保存
// tx.replace(R.id.id_content, fThree, "THREE"); //如果是replace 那么視圖重繪
tx.addToBackStack(null);
tx.commit();
Activity接收fragment數據
這個是fragment與Activity解耦的關鍵,如果你覺得這個fragment可以被復用,那么在fragment不要處理任何事件,全部以接口形式拋到Activity,在需要回調的activity實現這個接口,這么一來Fragment就如同一個空殼子,真正的邏輯都讓調用者Activity去做:
public class FragmentOne extends Fragment implements OnClickListener
{
private Button mBtn;
/**
* 設置按鈕點擊的回調
* @author zhy
*
*/
public interface FOneBtnClickListener
{
void onFOneBtnClick();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_one, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
mBtn.setOnClickListener(this);
return view;
}
/**
* 交給宿主Activity處理,如果它希望處理
*/
@Override
public void onClick(View v)
{
if (getActivity() instanceof FOneBtnClickListener)
{
((FOneBtnClickListener) getActivity()).onFOneBtnClick();
}
}
}
不同Activity的fragment間傳遞數據
依舊是一個簡單的場景:兩個Fragment,一個展示文章列表的Fragment(叫做ListTitleFragment),一個顯示詳細信息的Fragment(叫做:ContentFragment),當然了,這兩個Fragment都有其宿主Activity。
現在,我們點擊列表Fragment中的列表項,傳入相應的參數,去詳細信息的Fragment展示詳細的信息,在詳細信息頁面,用戶可以進行點評,當用戶點擊back以后,我們以往點評結果顯示在列表的Fragment對于的列表項中;
也就是說,我們點擊跳轉到對應Activity的Fragment中,并且希望它能夠返回參數,那么我們肯定是使用Fragment.startActivityForResult ;
在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,沒有setResult()方法,用于設置返回的intent,這樣我們就需要通過調用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
單個Fragment的Activity封裝
為何會有單個fragment的Activity,直接用Activity不行嗎?這是為以后的擴展,比如需求變了這個Activity界面需要再放兩個頁簽,像之前只有一個Activity就麻煩了,現在直接加fragment就完事!
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();
}
}
}