单Activity管理多个Fragment进行界面展示、页面跳转是很常见的界面编程方式,Fragment在使用时有一些使用技巧和“坑人”的地方,本文进行总结与分享。
Activity管理Fragment
Activity控制Fragment的展示、布局。
Fragment的展示和切换(出入栈)
通过FragmentManager管理Fragment出入栈,建议为每个Fragment定义一个tag(字符串常量),方便Fragment管理、Activity与Fragment通信,FragmentManager管理Fragment核心代码如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| public static final String TAG_LEAVE = "tag_leave"; public static final String TAG_LEAVE_APPLY = "tag_leave_apply";
private void navigateToTargetFragment(String tag, Bundle param) { BaseFragment targetFragment; if (TAG_LEAVE.equals(tag)) { if (leaveFragment == null) { leaveFragment = new LeaveFragment(); } targetFragment = leaveFragment; } else if (TAG_LEAVE_APPLY.equals(tag)) { if (leaveApplyFragment == null) { leaveApplyFragment = new LeaveApplyFragment(); } targetFragment = leaveApplyFragment; } else { if (leaveFragment == null) { leaveFragment = new LeaveFragment(); } targetFragment = leaveFragment; tag = TAG_LEAVE; } if (targetFragment == null || targetFragment.isVisible() || targetFragment.isAdded()) { return; } displayLayout(tag); if (param != null) { targetFragment.setArguments(param); } if (currentFragment == null) { fragmentManager.beginTransaction() .replace(R.id.fragment_container, targetFragment, tag) .addToBackStack(tag) .commit(); } else { fragmentManager.beginTransaction() .add(R.id.fragment_container, targetFragment, tag) .addToBackStack(tag) .commit(); } currentFragment = targetFragment; fragmentTag = tag; }
private void displayLayout(String tag) { setCommonRightIcon(R.drawable.common_transparent); switch (tag) { case TAG_LEAVE: { setCommonTitle(R.string.room_leave_resign_manage); setCommonRightIcon(R.drawable.action_bar_add); } break;
case TAG_LEAVE_APPLY: { setCommonTitle(R.string.room_leave_apply); } break;
default: } }
|
如上是Activity添加Fragment到当前界面,并在Fragment入栈时设置Activity UI。
通常情况下,当用户点击返回键(物理返回键、界面虚拟返回键)时,需要返回到上一个Activity而不是直接FinishActivity,而Fragment无法直接拦截物理返回键点击事件,因此需要通过宿主Activity来管理Fragment的出栈。核心代码在Activity,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override public void onBackPressed() { backFragment(); }
protected void backFragment() { fragmentManager.popBackStackImmediate(); if (fragmentManager.getBackStackEntryCount() == 0) { finish(); } else { fragmentTag = fragmentManager.getFragments().get(fragmentManager.getBackStackEntryCount() - 1).getTag(); displayLayout(fragmentTag); } }
|
Fragment通信
Fragment和Activity通信、Fragment之间通信(例如左右布局)
Fragment和Activity通信
- 通过接口回调实现Fragment向Activity的通信,宿主Activity实现通信接口,Fragment调用FragmentEvent的Activity实例传递事件和参数。通信接口类如下:
1 2 3 4 5 6 7 8 9 10 11
| public interface FragmentEvent {
Object onFragmentEvent(String event, Object param); }
|
Activity实现接口,Fragment获取实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class LeaveActivity extends BaseActivity implements FragmentEvent { ...
@Override public Object onFragmentEvent(String event, Object param) { if (CommonUtil.isEmpty(event)) { return null; } switch (event) { case TAG_LEAVE_DETAIL: { Bundle params = new Bundle(); params.putLong(LeaveDetailFragment.PARAM_LEAVE_ID, (Long) param); navigateToTargetFragment(TAG_LEAVE_DETAIL, params); } break;
case TITLE_TAG: { fragmentTag = param.toString(); displayLayout(fragmentTag); } break;
case BACK_TAG: { backFragment(); } break; } return null; }
... }
if (getActivity() instanceof FragmentEvent) { this.mFragmentEvent = (FragmentEvent) getActivity(); }
mFragmentEvent.onFragmentEvent(BACK_TAG, null);
|
- 通过广播,在Activity中注册广播,Fragment发送广播;
Activity向Fragment通信
通过fragmet的setArguments()方法,在fragment初始化的时候传递参数和事件;
Fragment中定义public方法,通过Activity中的fragment实例调用;
通过EventBus在activity中向fragment传递事;
Fragment之间通信
通过fragment的public方法,首先fragmentA通过getActivity().getFragmentManager().getFragment…()获取到fragmentB,然后调用fragmentB的public方法,比较繁琐。
使用接口(推荐),首先如上通过接口实现fragment向activity的通信,其次通过public方法实现activity向Fragment的通信,从而间接实现Fragment之间的通信。
使用setTargetFragment()、onActivityResult()、getTargetFragment()进行fragment间的通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| FragmentA { private void send(){ setTargetFragment(fargmentB, ...); }
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); ... } }
FragmentB { getTargetFragment().onActivityResult(requestCode, resultCode, Intent data); }
|
Fragment一些坑点
Fragment点击事件穿透
Fragment入栈后若没有被hide,上层Fragment的点击事件会被下发到下一层,通过拦截点击事件消除此影响。
在BaseFragment中为rootView设置点击属性,消化掉此层的点击事件,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (rootView == null) { rootView = inflater.inflate(this.getLayoutResId(), container, false); } if (rootView.getParent() != null) { ((ViewGroup) rootView.getParent()).removeView(rootView); } rootView.setClickable(true); ... }
|
参考
https://blog.csdn.net/u012881042/article/details/51798736
(完)