本文共 14486 字,大约阅读时间需要 48 分钟。
Fragment
可以在布局中直接使用<fragment>
标签,使用android:name
属性或者使用class
属性来指定对应的Fragment
实现。
activity_fragment_static
.xml布局文件
在Activity
中就直接使用布局就可以。
public class FragmentTestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment_static);//直接使用布局就好 }}
Fragment
除了在xml中使用<fragment>
标签来指定Fragment
外,还可以直接通过代码的形式来动态添加Fragment
到xml布局中。
动态添加Fragment
到xml中,需要借助FrameLayout
布局,具体实现方式,看下面代码。
activity_fragment_dynamic
.xml布局文件
FragmentTestActivity
中动态使用Fragment
替换FrameLayout
。
public class FragmentTestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment_dynamic); dynamicAddFragment(); } /** * 动态的使用Fragment */ private void dynamicAddFragment() { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); // 使用LiftCycleFragment替换指定的 id 的 View transaction.replace(R.id.content_dynamic_fragment, new DynamicFragment()); transaction.commit(); }}
Fragment
是依附于Activity的,所以生命周期和Activity
很多是保持一致的。当然也有一些不同的地方,让我们来看看吧。
Fragment
的生命周期图:
Fragment的生命周期和Activity的生命周期对比图
对于对象的创建和销毁问题,最好保持在2个相对应的生命周期中。
Fragment的API介绍:
// 通过bundle传递数据,后面会将用法setArguments(Bundle bundle);getArguments();// 判断Fragment状态isAdded();isDetached();isVisible();isHidden();isInLayout();isResumed();isRemoving();// 获取Fragment的标记和idgetId();getTag();//API 15getUserVisibleHint();setUserVisibleHint(true);// API 17getChildFragmentManager();getParentFragment();除了上述方法,还有Activity中的一些方法的封装和动画相关的方法。例如关于MenuItem,Permission,动画等,这里列举几个。如:MenuItem相关:setHasOptionsMenu(boolean b);onCreateOptionsMenu(Menu menu, MenuInflater inflater);onOptionsItemSelected(MenuItem item);Permission相关:requestPermissions(String[] permissions, int requestCode);onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);shouldShowRequestPermissionRationale(String permission);动画相关:API 21setEnterTransition(Transition transition);setExitTransition(Transition transition);setAllowEnterTransitionOverlap(boolean allow);setAllowReturnTransitionOverlap(boolean allow);
FragmentManager介绍:
getFragmentManager();// 获取FragmentManager方法findFragmentById();// 通过id获取对应的FragmentfindFragmentByTag();// 通过Tag获取对应的FragmentbeginTransaction();// 开启事务,返回FragmentTransaction对象popBackStack();// 退出栈顶的Fragment//退出指定id的FragmentpopBackStack(int id , int flag);// flag: 0 or FragmentManager.POP_BACK_STACK_INCLUSIVEpopBackStack(String tag , int flag);//退出指定Tag的FragmentpopBackStackImmediate();// 返回一个boolean值popBackStackImmediate(int id , int flag);popBackStackImmediate(String tag , int flag);getBackStackEntryCount();// 获取返回栈中的数量addOnBackStackChangedListener(); // 添加返回栈改变的监听removeOnBackStackChangedListener();// 移除返回栈的监听isDestroyed();// API 17 是否Activity走了OnDestory()生命周期方法
FragmentTransaction介绍:
FragmentTransaction
是一个抽象类,具体的是现实类是
BackStackRecord
。 常用API:
// 添加add(Fragment fragment, String tag);add(int containerViewId, Fragment fragment);add(int containerViewId, Fragment fragment,String tag);// 替换,相当于remove之后再addreplace(int containerViewId, Fragment fragment);replace(int containerViewId, Fragment fragment,String tag);// 移除remove(Fragment fragment);// 显示show(Fragment fragment);//隐藏hide(Fragment fragment);//依附attach(Fragment fragment);//分离detach(Fragment fragment);isEmpty();addToBackStack(String name);// 添加到任务栈,参name:null或者一个标记tag// 提交事务 API的具体差异,看源码解释commit();commitAllowingStateLoss();// 这是危险的,因为如果活动需要稍后从其状态恢复,则提交可能丢失commitNow();commitNowAllowingStateLoss();
Fragment
常常会和Activity
或Fragment
会有数据传递,那么如何实现呢
实现方式肯定有多种,就举例说下setArguments(Bundle bundle)
的方式传递消息:
public class ArgumentFragment extends Fragment { private String message; /** * 这种写法就是为了Activity或Fragment等向当前的Fragment传递消息 */ public static ArgumentFragment getInstance(String message) { //通过Bundle传递消息 Bundle bundle = new Bundle(); bundle.putString("message", message); ArgumentFragment fragment = new ArgumentFragment(); fragment.setArguments(bundle); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); //获取bundle传递的消息 Bundle bundle = getArguments(); if (bundle != null) { message = bundle.getString("message"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_lift_cycle, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextView textView = (TextView) view.findViewById(R.id.fragmnet_content_text); if (!TextUtils.isEmpty(message)) { textView.setText("ArgumentFragment message: "+message); } else { textView.setText("ArgumentFragment message is none"); } }}
在Activity
中的调用:
public class FragmentTestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); // 建议使用(不要直接new对象),这样可以传递信息给Fragment transaction.replace(R.id.content_dynamic_fragment, ArgumentFragment.getInstance("Argument")); transaction.commit(); }}
添加到Fragment返回栈是通过transaction.addToBackStack(String name)
方法,按返回键是,会从栈中一个个的退出。如果需要主动退出栈,可以调用fragmentManager.popBackStack()
退出返回栈。具体我们看代码:
public class FragmentTestActivity extends Activity { String[] tags = { "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment); createFragment(0, false); } private void createFragment(final int i, boolean addBackStace) { if (i >= tags.length) { return; } FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); BackStackFragment fragment = BackStackFragment.getInstance(tags[i]); fragment.setFragmentClickListener(new FragmentClickListener() { @Override public void onFragmentClick() { createFragment(i + 1, true); } }); transaction.replace(R.id.content_dynamic_fragment, fragment, tags[i]); if (addBackStace) { transaction.addToBackStack(tags[i]); } transaction.commit(); } @Override protected void onResume() { super.onResume(); computeClearBackStack(false); } @Override protected void onPause() { super.onPause(); computeClearBackStack(true); } private void computeClearBackStack(boolean clean) { FragmentManager fragmentManager = getFragmentManager(); int backStackEntryCount = fragmentManager.getBackStackEntryCount(); Log.e("FragmentTestActivity", "onPause--> backStackEntryCount: " + backStackEntryCount); for (int i = 0; i < backStackEntryCount; i++) { FragmentManager.BackStackEntry backStackEntryAt = fragmentManager.getBackStackEntryAt(i); String name = backStackEntryAt.getName(); Log.e("FragmentTestActivity", "onPause--> name: " + name); if (clean) { fragmentManager.popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); } } }}
BackStackFragment
public class BackStackFragment extends Fragment { private String message; private FragmentClickListener listener; public void setFragmentClickListener(FragmentClickListener listener) { this.listener = listener; } public static BackStackFragment getInstance(String message) { //通过Bundle传递消息 Bundle bundle = new Bundle(); bundle.putString("message", message); BackStackFragment fragment = new BackStackFragment(); fragment.setArguments(bundle); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); //获取bundle传递的消息 Bundle bundle = getArguments(); if (bundle != null) { message = bundle.getString("message"); } if (message == null) { message = ""; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_content, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextView textView = (TextView) view.findViewById(R.id.fragmnet_content_text); if (!TextUtils.isEmpty(message)) { textView.setText(message + " Fragment"); } else { textView.setText("Fragment"); } textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { listener.onFragmentClick(); } } }); }}
具体效果如图:
在Fragment
和ViewPager
结合使用时,ViewPager
会预加载下一页的内容,导致浪费用户的流量,所以需要通过懒加载的方式优化。当Fragment
的界面显示是,再去加载数据,具体实现如下:
LazyFragment
public abstract class LazyFragment extends Fragment { protected boolean isVisible; /** * 在这里实现Fragment数据的缓加载. */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } protected void onVisible() { lazyLoad(); } protected abstract void lazyLoad(); protected void onInvisible() { }}
MyLazyFragment实现
public class MyLazyFragment extends LazyFragment { private String message; // 标志位,标志已经初始化完成。 private boolean isPrepared; public static MyLazyFragment getInstance(String message) { Bundle bundle = new Bundle(); bundle.putString("message", message); MyLazyFragment firstFragment = new MyLazyFragment(); firstFragment.setArguments(bundle); return firstFragment; } @Override public void onAttach(Context context) { super.onAttach(context); Bundle bundle = getArguments(); if (bundle != null) { message = bundle.getString("message"); } if (message == null) { message = ""; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_content, container, false); // view.findViewById() 操作获取布局中的View对象 isPrepared = true; lazyLoad(); return view; } @Override protected void lazyLoad() { if (!isPrepared || !isVisible) { return; } //获取数据,填充控件 }}
前面讲的内容都是比较基础的知识,关于Fragment常见的异常和解决方案,可以参考下面的博客,总结的很好。
DialogFragment
是Fragment
的子类,同时具有Dialog
的特性,Google官方推荐使用,替代AlertDialog
。
DialogFragment
的使用和Fragment
是一样的,这里不做解释。这里讲讲DialogFragment
创建一个视图的2种方式。
onCreateDialog()
方法,创建Dialog
的视图public class AlertDiaolgFragment extends DialogFragment { public static void showDialog(FragmentManager manager) { FragmentTransaction transaction = manager.beginTransaction(); transaction.addToBackStack("Alert"); AlertDiaolgFragment diaolgFragment = new AlertDiaolgFragment(); diaolgFragment.show(transaction, "Alert"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { View view = LayoutInflater.from(getActivity()).inflate(R.layout.dialog, null); return new AlertDialog.Builder(getActivity()) .setTitle("Dialog") .setMessage("今天星期五") .setView(view) .setPositiveButton("OK", null) .setNegativeButton("Cancel", null) .create(); }}
onCreateView()
方法,直接加载视图,这里的用法就完全是Fragment
的用法public class MyDialogFragment extends DialogFragment { public static void showDialog(FragmentManager manager) { FragmentTransaction transaction = manager.beginTransaction(); transaction.addToBackStack("My"); AlertDiaolgFragment diaolgFragment = new AlertDiaolgFragment(); diaolgFragment.show(transaction, "My"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return LayoutInflater.from(getActivity()).inflate(R.layout.dialog, container,false); }}
DialogFragment
调用show()
方法导致Can not perform this action after onSaveInstanceState
异常的解决方案在使用DialogFragment
的时候,偶尔会遇到如下异常,在Activity
的生命周期方法走了onSaveInstanceState()
方法后,在调用DialogFragment
的show()
方法,导致异常:Can not perform this action after onSaveInstanceState
。
关于Can not perform this action after onSaveInstanceState
异常问题,我们的处理方式是:确保是在Activity
或Fragment
的处于onResume()
状态调用。在Fragment
中有isResume()
方法,通过判断该方法,是否显示Fragment
。在Activity
中,通过在onResume()
和onPause()
设置变量量控制。
以上面的MyDialogFragment
为例,如:
在Fragment中,需要显示MyDialogFragment的视图调用if(isResume()){ MyDialogFragment.showDialog(getFragmentManager);}在Activity中,需要显示MyDialogFragment的视图调用private boolean mState;if(mState){ MyDialogFragment.showDialog(getFragmentManager);}@Overrideprotected void onResume() { super.onResume(); mState = true;}@Overrideprotected void onPause() { super.onPause(); mState = false;}
转载地址:http://lesvn.baihongyu.com/