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阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容