创建实例
一般情况下我们创建Fragment可能都是像下面的做法:
OneFragment fragment = new OneFragment();
如果在创建时需要传递参数的话就是
OneFragment fragment = new OneFragment(xxx);
这样的话一般情况下是没问题的,但是一些特殊情况会出现问题。
比如当屏幕旋转
、内存吃紧
时,Activity会被回收
,重新创建
Activity,依附在Activity上的Fragment也会跟着重新创建
,Fragment会调用默认的无参构造函数
,这会导致无法执行有参构造函数
进行初始化工作。
解决方法:
用newInstance
的方法创建Fragment实例,然后在创建时把参数
放在Bundle
里边,setArguments()
传进去,然后在onCreate()
方法里边取出来。
这样即使出现上述的特殊情况,也能在onCreate()
方法里把 参数
取出来进行初始化工作。
public static OneFragment newInstance(int args){
OneFragment oneFragment = new OneFragment();
Bundle bundle = new Bundle();
bundle.putInt("someArgs", args);
oneFragment.setArguments(bundle);
return oneFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
int args = bundle.getInt("someArgs");
}
通信方式
通常存在3种通信场景:
- Activity 操作内嵌的Fragment
- Fragment 操作宿主Activity
- Fragment 操作同级的 Fragment
一般的情况下:
- 我们在Activity中创建的
Fragment
,所以自然会持有Fragment
的对象实例
,或者通过findFragmentById()
和findFragmentByTag()
方法都能获取到Fragment
的对象实例
,所以可以操作到Fragment
。 - Fragment通过
getActivity()
方法可以获取到宿主Activity对象
,进而可以操作
宿主Activity
。 - 既然通过
getActivity()
方法就可以获取到Activity,那自然也就可以操作
其他的Fragment对象
问题:
虽然上述方法可以解决所有的通信问题,但会造成代码逻辑紊乱
的情况,不符合高内聚
,低耦合
的编程思想。
Fragment做好自己的事情即可,所有涉及到Fragment之间的控制显示等操作,应该由Activity统一管理。
解决方法:
通过对外开放接口的形式,将Fragment对Activity的一些操作,由Activity来管理。相当于Fragment操作Activity,但是Fragment只提供一个接口,让Activity自个自觉的把Fragment需要操作的方法放进去。
实现方式如下:
public class OneFragment extends Fragment implements View.OnClickListener{
public interface IOneFragmentClickListener{
void onOneFragmentClick();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_one, null);
contentView.findViewById(R.id.edt_one).setOnClickListener(this);
return contentView;
}
@Override
public void onClick(View v) {
if (getActivity() instanceof IOneFragmentClickListener){
((IOneFragmentClickListener) getActivity()).onOneFragmentClick();
}
}
}
只要在宿主 Activity 实现 Fragment 定义的对外接口 IOneFragmentClickListener,便可以实现 Fragment 调用 Activity 的功能。
也可以这样:
public class OneFragment extends Fragment implements View.OnClickListener{
private IOneFragmentClickListener clickListener;
public interface IOneFragmentClickListener{
void onOneFragmentClick();
}
public void setClickListener(IOneFragmentClickListener clickListener) {
this.clickListener = clickListener;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment_one, null);
contentView.findViewById(R.id.edt_one).setOnClickListener(this);
return contentView;
}
@Override
public void onClick(View v) {
clickListener.onOneFragmentClick();
}
}
原理是一样的,只是相比第一种方式,需要在宿主 Activity 中额外添加一步监听设置:
oneFragment.setClickListener(this);
Fragment重叠问题
原因:
情况1:重复创建实例导致重叠
一般情况下我们是在Activity
的onCreate()
或Fragment
的onCreateView()
里加载Fragment,如果遇到页面重启
的情况(比如屏幕旋转、内存回收、更换字体等情况被强杀重启),由于系统的保存机制
,会自动帮我们保存Fragment的状态
,然后重启以后帮我们恢复
,但是在我们的onCreate()或onCreateView()方法内又重新创建add
了一次,所以导致重叠
。情况2:没有保存mHidden状态导致重叠
mHidden
参数用于保存Fragment
的显示状态
(mHidden=ture代表隐藏,mHidden=false代表显示),比如你有2个Fragment,一个为ture状态,一个是false状态,此时遇到界面重启的情况,系统会自动
帮你恢复Fragment
,这时由于mHidden状态没有保存
,所有的Fragment默认mHidden都是false(即显示状态
),所以会导致重叠
。
</br>
情况 2
在v4-24.0.0+ 开始,官方修复了上述 没有保存mHidden的问题,所以如果你在使用24.0.0+的v4包,即可不考虑情况 2 的问题。
但是! 如果你用的不是V4包的Fragment,而是android.app.Fragment包的Fragment,那么问题依然是存在的!
解决办法:
- 情况1:
直接在Fragment的onCreate()内加个判断:
//为空说明是首次加载,不是页面重启的情况
if (savedInstanceState == null){
//进行初始化工作
}
</br>
- 情况2:
手动维护一个变量mSupportHidden,用于保存每个Fragment自己的显示状态,结合情况1、2的解决办法代码:
public class BaseFragment extends Fragment {
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (isSupportHidden) {
ft.hide(this);
} else {
ft.show(this);
}
ft.commit();
}
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
}
remove()方法不能出栈
还需实践踩坑...
getActivity()空指针
还需实践踩坑...
多个Fragment同时出栈的深坑BUG
还需实践踩坑...
深坑 Fragment转场动画(仅分析v4包下的Fragment)
还需实践踩坑...