fragment最佳實踐

前言

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();  
        }  
    }  

}  
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容