Fragment知识点全解析(二)

Fragment 回退栈案例解析

这里主要讲下fragment回退栈的案例解析。当然,fragment本身还是有很多坑的,通常的选项卡一层套用fragment还不至于遇到这些坑,但是随着fragment层次深入,就很容易中奖~

案例情景
activityC内嵌套一个fragmentOne,然后从fragmentOne跳到fragmentTwo。

activityC内代码如下:

public class ActivityC extends BaseActivity {
    private final static String TAG = "ActivityC";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_c);

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        FragmentOne fragmentB = new FragmentOne();
        fragmentTransaction.add(R.id.content_layout, fragmentB);
        fragmentTransaction.commit();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        Log.d(TAG, "————————————————");
    }
}

fragmentOne内跳转到fragmentTwo的跳转代码如下:

 @OnClick(R.id.edit_bt)
    public void onClick() {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        FragmentTwo fragmentTwo = new FragmentTwo();
        fragmentTransaction.replace(R.id.content_layout, fragmentTwo);
        fragmentTransaction.commit();
    }

这里用的是fragmentTransaction.replace和fragmentTransaction.commit();而fragmentTransaction.replace是remove和add的结合,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。

此处FragmentOne跳转到FragmentTwo的log日志如下:

D/FragmentOne: onPause
D/FragmentOne: onStop
D/FragmentOne: onDestroyView
D/FragmentOne: onDestroy
D/FragmentOne: onDetach
D/FragmentTwo: onViewCreated
D/FragmentTwo: onStart
D/FragmentTwo: onResume

若加上回退栈,fragmentTransaction.addToBackStack(null); 此处FragmentOne跳转到FragmentTwo的log日志如下:

D/FragmentOne: onPause
D/FragmentOne: onStop
D/FragmentOne: onDestroyView
D/FragmentTwo: onCreateView
D/FragmentTwo: onStart
D/FragmentTwo: onResume

说明加上了回退栈,fragmentOne未执行onDestroy和onDetach。只是对视图view实例进行销毁。

此时在fragmentTwo中点击返回键,可以正常返回到fragmentOne,并且可以看到fragmentOne之前输入的数据仍在。
在FragmentTwo点击返回键后显示FragmentOne了的log日志如下:

D/FragmentTwo: onPause
D/FragmentTwo: onStop
D/FragmentTwo: onDestroyView
D/FragmentTwo: onDestroy
D/FragmentTwo: onDetach
D/FragmentOne: onCreateView
D/FragmentOne: onStart
D/FragmentOne: onResume

说明之前对FragmentOne的视图层次销毁后,再次返回到FragmentOne时执行了onCreateView,进行视图重建。由于getFragmentManager本身会对栈内的fragment的状态进行保存,这里找到栈内的对应的fragment就可以恢复。

下面看下动图~

这里写图片描述

BaseFragment的封装优化

下面我们看下对回退栈的封装和“一activity多fragment”模式的运用。
1. 同级式fragment可以采用单独的一个activity和里面多个平级的fragment。
2. 流程式fragment可以采用activity和fragment绑定着使用。

此处的封装是针对第二种情况,在看源码之前大家可以先看下此篇博客,

http://blog.csdn.net/tyk0910/article/details/51355026

案例采用的是getSupportFragmentManager,所以引入了V4包。

案例:activityC内嵌套一个fragmentOne,然后从fragmentOne跳到fragmentTwo,从fragmentTwo跳到fragmentThree。

BaseFragmentActivity源码如下:

@SuppressWarnings("RestrictedApi")
public abstract class BaseFragmentActivity extends FragmentActivity {

    //获取第一个fragment
    protected abstract BaseFragment getFirstFragment();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);
        getActionBar().hide();
        //避免重复添加Fragment
        if (null == getSupportFragmentManager().getFragments()) {
            BaseFragment firstFragment = getFirstFragment();
            if (null != firstFragment) {
                addFragment(firstFragment);
            }
        }
    }

    //添加fragment
    public void addFragment(BaseFragment fragment) {
        if (fragment != null) {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.content_layout, fragment, fragment.getClass().getSimpleName())
                    .addToBackStack(fragment.getClass().getSimpleName())
                    .commitAllowingStateLoss();
        }
    }

    //移除fragment
    public void removeFragment() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
            getSupportFragmentManager().popBackStack();
            Log.d("removeFragment", "fragment回退栈中的个数=" + getSupportFragmentManager().getBackStackEntryCount());
        } else {
            finish();
        }
    }

    //返回键返回事件
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (KeyEvent.KEYCODE_BACK == keyCode) {
            if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
                finish();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
}

getFirstFragment()是获取首次嵌套的fragment实例。

onCreate方法里直接setContentView了布局,这是一个所有activity的根布局,因为继承BaseFragmentActivity的activity都是要嵌套fragment的,所以统一的根布局不会造成过度绘制。

addFragment方法里,以replace的形式替换fragment,并且增加了加入回退栈addToBackStack方法,commitAllowingStateLoss是为了避免状态丢失报错。

removeFragment首先判断回退栈内的fragment个数,若是大于1就抛出一个。否则说明已经剩下最后一个fragment了,可以执行activity的finish了。

BaseFragment的代码如下:

public abstract class BaseFragment extends Fragment {
    protected BaseFragmentActivity baseActivity;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.baseActivity = (BaseFragmentActivity) context;
    }

    protected abstract int getLayoutId();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(getLayoutId(), container, false);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // TODO: 2017/3/8 此阶段fragment是独立生命阶段,可做些通用的操作,如进度条的初始化等
    }


    //添加fragment
    protected void addFragment(BaseFragment fragment) {
        if (null != fragment) {
            baseActivity.addFragment(fragment);
        }
    }

    //移除fragment
    protected void removeFragment() {
        baseActivity.removeFragment();
    }

}

onAttach方法里面获取依附的activity实例,避免了用getActivity()为Null的情况。

getLayoutId抽象方法获取子fragment的布局。

添加和删除fragment的方法是调用activity的,这里是为了在多层嵌套fragment的添加或返回的时候调用。

activity中的源码如下:

public class ActivityC extends BaseFragmentActivity {

    @Override
    protected BaseFragment getFirstFragment() {
        return FragmentOne.newInstance(getClass().getSimpleName());
    }
}

activity中只需要通过getFirstFragment获取fragment实例即可。非常的轻~以后更换界面只需要更换fragment就可以了。

子fragment的源码如下:

public class FragmentOne extends BaseFragment {
    private final static String TAG = "FragmentOne";
    @BindView(R.id.show_info)
    EditText showInfo;
    @BindView(R.id.edit_bt)
    Button editBt;
    @BindView(R.id.back_bt)
    Button backBt;

    @Override
    protected int getLayoutId() {
        return R.layout.fragment_one;
    }

    public static FragmentOne newInstance(String msg) {
        FragmentOne fragmentOne = new FragmentOne();
        Bundle bundle = new Bundle();
        bundle.putString(TAG, msg);
        fragmentOne.setArguments(bundle);
        return fragmentOne;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = super.onCreateView(inflater, container, savedInstanceState);
        ButterKnife.bind(this, rootView);
        return rootView;
    }

    @OnClick({R.id.edit_bt, R.id.back_bt})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.edit_bt:
                addFragment(FragmentTwo.newInstance(TAG));
                break;
            case R.id.back_bt:
                removeFragment();
                break;
        }
    }
}

newInstance方法是对fragment的初始化,并且通过setArguments传参。

这里结合ButterKnife的运用,省去findviewById和setOnClickListener等代码。

Activity与Fragment之间通信

推荐方式:

  1. 因为所有的Fragment都是依附于Activity的,并且每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()在activity中获得任何Fragment实例,从而调用依附于它的fragment内的方法(activity内有fragment引用时可直接用)。
  2. 在fragment里声明接口,在相应的activity内实现此接口即可。这是最佳方法,考虑到fragment的复用性和activity与fragment之间的解耦。

可选方式:

  1. 在activity与fragment之间通过handler收发消息建立通信。缺点:容易引起内存泄漏。

  2. 通过广播建立通信。缺点:有些大材小用了,广播多用于一对多的情景。

  3. 通过EventBus建立通信。缺点:用多了,代码可读性会差,团队协作时维护成本高。

源码下载

参考文献:

http://blog.csdn.net/tyk0910/article/details/51355026
http://www.jianshu.com/p/d9143a92ad94
http://blog.csdn.net/lmj623565791/article/details/37992017

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值