<meta charset="utf-8">
<article class="_2rhmJa">
new 一个Fragment,Fragment只是调用了自身的空参数构造方法,并没有其他操作。
* Fragment要执行其onAttach()及其之后的生命周期方法,需要被FragmentTransition记录并真正提交到FragmentManager处,才能实现。
* 如果Fragment要准备显示了(即被事务提交到FragmentManager,具体后面会说到),那么它再继续执行onCreateView()到onResume()的方法,去创建和绑定UI,最后在前台显示。
* 由于ViewPager在Activity.onCreate()中并没有真正初始化界面完成,即在onCreate()阶段中,ViewPager适配器内部并没有真的调用Fragment的构造方法并从FragmentManager中获取FragmentTransaction事务来记录Fragment的操作,等到Activity.onResume()方法被执行(即Activity启动完成并在前台显示了),这时,ViewPager才选取默认要显示的那一页,并对应执行这一页的Fragment的创建+事务添加+提交给Manager的一个过程。
* 在Activity被停止或即将被销毁的过程中,都首先停止或销毁它内部的所有Fragment,而它内部的所有Fragment则各自检查自己是否有置于前台或显示,如果有,那就得destroyView()销毁掉它所绑定的View布局并使自己处于完全不可见状态,再判断地执行onDestroy()。
* 对于最后一个例子,Fragment执行自身的startActivityForResult()和getActivity().startActivityForResult()是有区别的:如果执行自身的startActivityForResult(),那么在另一个Activity返回时,Fragment就会执行自身的onActivityResult()方法,否则,只会触发Activity的onActivityResult(),除非你在Activity的onActivityResult()方法中添加几行代码,让它把结果也返回给指定Fragment,如:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (fragmentA!=null)
fragmentA.onActivityResult(requestCode,resultCode,data);
if (fragmentB!=null)
fragmentB.onActivityResult(requestCode,resultCode,data);
}
Fragment开发遇到的问题和解决(Fragment注意点)
一、Fragment 与 后台事务栈管理
前面说了Fragment的几个生命周期方法可能不止执行一次,关键点就在于Fragment是否被事务管理到后台栈中,这里就涉及到与Fragment有关的几个类的相关方法:
- FragmentTransaction.addToBackStack(String)【关键:让携带着Fragment记录的事务保存到后台栈中】
- FragmentActivity.onBackPressed()【点击BACK按键或者其他方式触发Activity的回退方法时,会促发此方法,FragmentActivity的此方法中会多加一个判断,看后台栈中是否存在事务,存在,则一个事务出栈,这个事务对应的Fragment操作记录则被回退清空,也就是这个事务下Fragment的操作,全部撤销,Fragment会从FragmentManager维护的Fragment队列中拿出并被销毁从而执行它的生命周期的余下方法】
- FragmentManager.popBackStack()【Manager管理的后台栈的一个出栈操作,返回一个FragmentTransaction事务,但是此操作要等到Application返回它是事件loop时才会触发】
- FragmentManager.popBackStackImmediate()【与popBackStack()方法差不多,只是它是立即执行出栈操作,而不用顾忌Application】
下面举个例子,给大家看看Fragment是怎么跑的:
【这里使用FragmentTransaction.replace()作为主要Fragment操作】
原因:
add()方法与replace()方法大多数情况下效果是一样的,add是指“添加”,replace是指“替换”,一般使用同一个FrameLayout去加载Fragment时,推荐是用replace()的,省去add的多层Fragment重叠,当然,在需要进行轮播等需要及时看到多个Fragment的时候,add()比较好的。
remove()、hide()、show()、detach()、attach()等方法,都相应地只是执行对应Fragment的对应生命周期方法而已
所以这里用replace()做演示更好。
主要执行的代码:
/***
* 方式一:单个Fragment 做下记录到backStack
*
* @param baseFragment
*/
public void onAddFragment(Fragment baseFragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.frame_root_fragment, baseFragment, "tag");
ft.addToBackStack("tag");
ft.commit();
}
/**
* 方式二:多个Fragment同时addToBackStack
*
* @param fragmentList
*/
public void onAddFragments(Fragment[] fragmentList) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
int pos = 0;
for (Fragment item : fragmentList) {
pos += 1;
ft.replace(R.id.frame_root_fragment, item, "tag" + pos);
ft.addToBackStack("tag" + pos);
}
ft.commit();
}
gif图:
-
执行方式一代码(事务一对一Fragment)
[图片上传失败...(image-a1a51a-1594347282698)]
-
Log截图:
[图片上传失败...(image-5378c-1594347282698)]
-
执行方式二代码(事务一对多Fragments)
[图片上传失败...(image-9193fe-1594347282698)]
-
Log截图:
[图片上传失败...(image-a7b73b-1594347282698)]
得出结论:
- 从代码可以看到,FragmentTransaction可以记录一到多个Fragment的相关操作。
- 示例可以知道,FragmentActivity回退事件发生时,会先把所有的FragmentTransaction事务一一弹出后台栈先。【如果一个事务对应一个Fragment,那么这里就实现了一个Fragment之间的跳转过程;而如果一个事务对应多个Fragment,那么,一个事务弹出,它涉及到的后台队列中Fragment集合便会一下子都弹出销毁,而不是一个个Fragment地出队】
二、Fragment以及它的宿主Activity的复用
在平时开发中,怎么样方便开发、方便维护、尽量解耦,就怎么写代码,这里给一个比较好的Fragment写法例子(参考鸿洋博客中的一个Fragment例子),供参考:
public class ContentFragment extends Fragment
{
private String mArgument;///Activity传递的数据(值)
public static final String ARGUMENT = "argument";///Activity传递的数据名(键)
public static final String RESPONSE = "response";///Activity
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
Intent intent = new Intent();
intent.putExtra(RESPONSE, "good");
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
}
}
////在实例化时获取Activity传入的值(这里示例为String类型)
public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle);
return contentFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
Random random = new Random();
TextView tv = new TextView(getActivity());
///.........
return tv;
}
}
其实写法不一,可能有很多更加灵活的写法,但本文不深究,只要大家有“Fragment的复用”这个思想,旨在写出容易复用、与Activity耦合度小、存在与外界通信接口的Fragment,就能够实际减少工作量、理清思路。
下面是一个抽象Activity,用于简单状态Fragment 的Activity自身代码的复用,可参考:
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();
}
}
}
三、关于DialogFragment
Android3.0被引入的一类特殊Fragment,方便我们构建具有和Fragment一样生命周期的一类Dialog等组件,从而解决普通的Dialog等组件难以管理它的生命周期、与Activity、Fragment交互的限制。详细可参考Android 官方推荐 : DialogFragment 创建对话框
四、Fragment与外界的通信
理解了Fragment生命周期和它的基本写法,那么,再说说Fragment与外界的通信。上面提到的DialogFragment就是用于优化通信和管理的,那么,平常我们使用Fragment,应该如何保证与其他Fragment和外部Activity进行通信呢?
- Fragment与它的宿主Activity通信:
* (Activity传给Fragment)Activity通过`Fragment.setArguments(Bundle)`在创建Fragment时传递数据给Fragment,在Fragment的onCreate()中,Fragment通过`getArguments()`获取Bundle数据。
* (Activity传给Fragment)Activity通过`getSupportFragmentManager().findFragmentById()`或`getSupportFragmentManager().findFragmentByTag()`获取Fragment并调用Fragment自己定义的方法,数据通过参数形式传给Fragment。
* (Fragment传给Activity)Fragment通过`getActivity()`方式获取宿主Activity,就可以调用Activity的方法【但是这样不严谨,Fragment能够调用的方法,应该要受到限定,所以,使用如下代码的方式,通过在Fragment中定义一个接口,然后让宿主Activity实现这些方法】
public static class FragmentA extends ListFragment{
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
try{
mListener =(OnArticleSelectedListener)activity;
}catch(ClassCastException e){
throw new ClassCastException(activity.toString()+"must implement OnArticleSelectedListener");
}
}
...
`* (Activity传给Fragment、Fragment传Fragment)还可以通过广播的方式,让Fragment去注册广播,然后,Fragment调用`getActivity().sendBroadcast(Intent)`或者Activity调用`sendBroadcast(Intent)`来发出广播,让注册了该广播的Fragment去接收并过滤广播信息【然而这样做有些小题大做,而且需考虑广播的注册等】 *<u>关键代码</u>*:`
public class FragmentA extends Fragment{
MyBroadcast broadcast;
//.........
@Override
public View onCreateView(LayoutInflater inflater
, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//........
IntentFilter filter = new IntentFilter();
try {
if (mReceiver != null) {
getActivity().unregisterReceiver(broadcast);
}
} catch (Exception e) {
e.printStackTrace();
}
broadcast = new MyBroadcast();
filter.addAction(PageOneFragment.DATA_CHANGED);
getActivity().registerReceiver(broadcast, filter);
//........
}
//..................
class MyBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(PageOneFragment.DATA_CHANGED)) {
///。。。。。
}
}
}
}
- Fragment之间的通信:
* 在同一个Activity下的两个Fragment的通信:FragmentA调用其宿主Activity的方法,宿主Activity再根据FragmentA的调用参数去调用FragmentB的方法并传递参数给B。
* 不同Activity下的两个Fragment的通信:
* 首先能够保证两者都能够执行到onActivityResult()方法【文章前面有说到】
* (A传给B)FragmentA通过startActivityForResult()【调用Fragment本身或者getActivity()都可以】的方式,把Intent数据等传到另一个Activity,然后让另一个Activity传值给FragmentB
* (B返回给A)FragmentB处理完,通过getActivity().setResult()把返回的数据设置好,然后返回Activity,A再从它的onActivityResult中拿。
* 同一个Activity下,DialogFragment 与 Fragment的交互:
> 原理:FragmentA中new一个DialogFragment对象,并让其`setTargetFragment()`来绑定目的Fragment,等DialogFragment处理完数据,调用刚刚绑定的FragmentA的`onActivityResult()`把数据传回给FragmetnA。
上关键代码:
*<u>Fragment 的代码</u>*
public class ContentFragment extends Fragment
{
//...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//....
tv.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
EvaluateDialog dialog = new EvaluateDialog();
//注意setTargetFragment
dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
dialog.show(getFragmentManager(), EVALUATE_DIALOG);
}
});
return tv;
}
//接收返回回来的数据
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_EVALUATE)
{
String evaluate = data
.getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.putExtra(RESPONSE, evaluate);
getActivity().setResult(Activity.REQUEST_OK, intent);
}
}
}
*<u>DialogFragment 的代码</u>*
```
public class EvaluateDialog extends DialogFragment
{
private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" };
public static final String RESPONSE_EVALUATE = "response_evaluate";
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Evaluate :").setItems(mEvaluteVals,
new OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
setResult(which);
}
});
return builder.create();
}
// 设置返回数据
protected void setResult(int which)
{
// 判断是否设置了targetFragment
if (getTargetFragment() == null)
return;
Intent intent = new Intent();
intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
Activity.RESULT_OK, intent);
}
}
> 补充:关于DialogFragment调整窗口大小,可以参考[这篇文章](https://link.jianshu.com?t=http://blog.csdn.net/angcyo/article/details/50613084)
## 感谢阅读
* * *
对于Fragment的小小讲解,就到此结束了,之后会慢慢深入,谢谢大家支持,喜欢的读者可以关注我一波~~
推荐阅读:
本人的几篇文章:
[Android开发细节--查漏补缺(二):易忘难懂](https://www.jianshu.com/p/c0a4fa16b8d6)
[Java面试相关(一)-- Java类加载全过程](https://www.jianshu.com/p/ace2aa692f96)
Fragment相关文章:
[http://blog.csdn.net/lmj623565791/article/details/37815413](https://link.jianshu.com?t=http://blog.csdn.net/lmj623565791/article/details/37815413)
[http://blog.csdn.net/lmj623565791/article/details/42628537/](https://link.jianshu.com?t=http://blog.csdn.net/lmj623565791/article/details/42628537/)
[http://www.cnblogs.com/android-joker/p/4414891.html](https://link.jianshu.com?t=http://www.cnblogs.com/android-joker/p/4414891.html)
</article>
104人点赞
[Android组件与UI](/nb/5332097)
[https://www.jianshu.com/p/1ff18ec1fb6b](https://www.jianshu.com/p/1ff18ec1fb6b)