Fragment
已经成为Android
开发界面设计中不可或缺的一部分,同时也发挥着愈来愈重要的角色,虽然Fragment
已经能出色的项目开发,可是在使用过程当中也暴露了愈来愈多的问题,虽然google
也一直在及时的修复,可是仍是有不少坑,因此决定记录Fragment
使用过程当中的使用问题,避免小伙伴们重复踩坑。java
在了解踩坑以前,咱们须要先了解Fragment
的使用要点和使用方法android
Fragment
介绍做为 view
界面的一部分,Fragment
的存在必须依附于 FragmentActivit
使用,而且与 FragmentActivit
同样,拥有本身的独立的生命周期,同时处理用户的交互动做。同一个 FragmentActivit
能够有一个或多个 Fragment
做为界面内容,一样Fragment
也能够拥有多个子Fragment
,而且能够动态添加、删除 Fragment
,让UI
的重复利用率和易修改性得以提高,一样能够用来解决部分屏幕适配问题。git
另外一方面,support v4 包中也提供了 Fragment,兼容 Android 3.0 以前的系统,使用兼容包须要注意两点:github
宿主Activity
必须继承自 FragmentActivity
;网络
使用getSupportFragmentManager()
方法获取 FragmentManager
对象;异步
Fragment
一样是具有了独立的生命周期,可是和Activity
的生命周期还有不同的地方,如图:原图地址ide
Fragment
初始化Fragment
默认有两种初始化的方法,一种new
另外一种是嵌入xml
动画
new
this
FirstFragment firstFragment=new FirstFragment();复制代码
xml
google
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.wzgiceman.fragmentpit.Fragment.FirstFragment"/>复制代码
上面两种方法均可以初始获得一个Fragment
对象,可是前者比后者的有点在于,前者更加的灵活,因此推荐使用第一种方式。
Activity
和Fragment
传参默认建立Fragment
系统已经给咱们初始了传参的代码
/** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 Parameter 1. * @param param2 Parameter 2. * @return A new instance of fragment FirstFragment. */
// TODO: Rename and change types and number of parameters
public static FirstFragment newInstance(String param1, String param2) {
FirstFragment fragment = new FirstFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}复制代码
这无疑是最好的选择
Fragment
类提供有startActivityForResult()
方法用于 Activity
间的页面跳转和数据回传,其实内部也是调用 Activity
的对应方法。可是在页面返回时须要注意 Fragment 没有提供 setResult()
方法,能够经过宿主 Activity
实现。
FragmentManager
和FragmentTransaction
使用FragmentManager
在Activity
中使用Fragment
可使用getSupportFragmentManager
获取一个FragmentManager
对象,可是在Fragment
中显示子Fragment
须要调用Fragment
的getChildFragmentManager()
源码以下:
public final FragmentManager getChildFragmentManager() {
throw new RuntimeException("Stub!");
}复制代码
FragmentTransaction
Fragment
的动态添加、删除等操做都须要借助于 FragmentTransaction
类来完成,好比上面提到的 commit()
操做,下面是几种经常使用的方法:
add() 系列:添加 Fragment 到 Activity 界面中;
remove():移除 Activity 中的指定 Fragment;
replace() 系列:经过内部调用 remove() 和 add() 完成 Fragment 的修改;
hide() 和 show():隐藏和显示 Activity 中的 Fragment;
addToBackStack():添加当前事务到回退栈中,即当按下返回键时,界面回归到当前事物状态;
commit():提交事务,全部经过上述方法对 Fragment 的改动都必须经过调用 commit() 方法完成提交
replace()
和hide()
区别
replace()
和hide()
均可以动态的在Activity
中显示多个Fragment
,而且能够来回灵活的切换,可是它们有很大的区别,replace()
方法不会保留 Fragment
的状态,也就是说诸如 EditText
内容输入等用户操做在 remove()
时会消失;可是hide()
却不会,能完整的保留用户的处理信息。
addToBackStack()
退栈
当用户按下返回键时,若是回退栈中保存有以前的事务,会先执行事务回退,而后再执行Activity
的finish()
方法 。
经过FragmentManager
和FragmentTransaction
结合使用,咱们能够将第一种初始化的Fragment
动态的显示到界面中,这里使用replace()
演示:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fl_fragment, FirstFragment.newInstance("A","B"));
ft.commit();复制代码
在了解了Fragment
的基础使用后,能够开始使用过程当中的踩坑了
getActivity()
引用问题在Fragment
中经常须要使用到content
对象,好比网络加载如今一个progress
等等,这时候可能你遇到过getActivity()
返回null
,或者平时运行无缺的代码,在“内存重启”以后,调用getActivity()的地方却返回null
,报了空指针异常。
大多数状况下的缘由:你在调用了getActivity()
时,当前的Fragment已经onDetach()
了宿主Activity
。
好比:你在pop
了Fragment
以后,该Fragment
的异步任务仍然在执行,而且在执行完成后调用了getActivity()
方法,这样就会空指针;
用getContext()
替代getActivity()
定义全局变量,在Fragment
的onAttach(Activity activity)准备废弃
或者onAttach(Context context)
方法中初始化
Context context;
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.context=context;
}复制代码
显然第一种方法更加灵活方便了。
当子Fragment
须要调用宿主Acitivity
的方法时,好比子Fragment
须要发送一个广播,可是Fragment
没有改方法,因此须要借助宿主Activity
去发送,这时候经常须要强制转换content
对象,而后调用宿主Acitivity
发方发送广播,这种直接使用的方式违背了高聚低耦的设计原则;
经过接口抽象的方法,经过接口去调用宿主Activity的方法。
/** * 发送广播 * Created by WZG on 2016/12/31. */
public interface SendBListener {
void send();
}复制代码
public class FirstFragment extends Fragment {
SendBListener listener;
public void setListener(SendBListener listener) {
this.listener = listener;
}
@OnClick(value = R.id.tv)
void onTvClick(View view) {
listener.send();
}
}复制代码
public class MainActivity extends AppCompatActivity implements SendBListener{
@BindView(R.id.fl_fragment)
FrameLayout mFlFragment;
@Override
public void send() {
sendBroadcast(new Intent("xxxxxx"));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirstFragment firstFragment=new FirstFragment();
firstFragment.setListener(this);
}
}复制代码
因为采用建立对象的方式去初始化Fragment
对象,当宿主Activity
在界面销毁或者界面从新执行onCreate()
方法时,就有可能再一次的执行Fragment
的建立初始,而以前已经存在的 Fragment 实例也会销毁再次建立,这不就与 Activity 中 onCreate() 方法里面第二次建立的 Fragment 同时显示从而发生 UI 重叠的问题。
若是宿主界面Acitivity
能够横竖屏切换,致使的生命周期从新刷新也同理可致使界面的重叠问题。
savedInstanceState
判断@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
FirstFragment firstFragment;
if (savedInstanceState==null) {
firstFragment=new FirstFragment();
ft.add(R.id.fl_fragment, firstFragment, "FirstFragment");
}else {
firstFragment = (FirstFragment) fm.findFragmentByTag("FirstFragment");
}
}复制代码
Activity
提供的 onAttachFragment()
方法中处理@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment instanceof FirstFragment){
firstFragment = (FirstFragment) fragment;
}
}复制代码
Fragment
时判断Fragment fragment = getSupportFragmentManager().findFragmentByTag("FirstFragment");
if (fragment==null) {
firstFragment =new FirstFragment();
ft.add(R.id.fl_fragment, firstFragment, "FirstFragment");
}else {
firstFragment = (FirstFragment) fragment;
}复制代码
Fragment
转场动画若是你想给下一个Fragment设置进栈动画和出栈动画,setCustomAnimations(enter, exit)
只能设置进栈动画,第二个参数并非设置出栈动画;
请使用setCustomAnimations(enter, exit, popEnter, popExit)
,这个方法的第1个参数对应进栈动画,第4个参数对应出栈动画,因此是setCustomAnimations(进, exit, popEnter, 出))
Fragment
状态监听不少时候,咱们须要在多Fragment
中刷新界面,固然因为Fragment
有本身独立的生命周期可是也依赖宿主Activity
存在,因此在刷新界面的时候须要注意如:
当宿主Activity
A
进入B
中,又冲B
返回到A
,这时候宿主A
执行onResume()
方法,固然这时候的Fragment
也会执行onResume()
当宿主Activity
A
中的Fragment
所有初始完成显示过,在切换Fragment
的时候不会再一次触发onResume()
方法,可是却能够触发Fragment的onHiddenChanged(boolean hidden)
方法
因此当咱们须要实时刷新Fragment
界面的时候,须要同时结合onResume()
和onHiddenChanged(boolean hidden)
方法去刷新当前显示Fragment
而避免刷新hide()
的Fragment
使用
Override public void onResume() {
super.onResume();
//当前是不是现实状态
if (isVisible()){
//刷新界面
updateUI();
}
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
//方法重复发起刷新界面
if (isVisible() && isResumed()){
updateUI();
}
}复制代码