您的位置:首页 > 其它

Fragment总结

2017-02-16 18:46 120 查看

零、前言

今年可能要实习了,准备把之前学过的知识点重头开始回顾下,不学习新东西了,这篇讲Fragment。

一、生命周期



onAttach(Activity activity)

当Fragment第一次被添加到宿主Activity上时调用。

onCreate(@Nullable Bundle savedInstanceState)

初始化Fragment时调用,调用时期:在onAttach()方法之后,在onCreateView()方法之前。

onCreateView(inflater,container,savedInstanceState)

当已经有了一个fragment实例,一般我们在这里创建Fragment的视图。

在onCreate()之后,onActivityCreated(Bundle)之前调用。

如果在这里返回了一个View,那么当在释放时将会调用onDestroyView().

onActivityCreated(Bundle)

当Fragment的宿主Activity已经被create()了并且这个Fragment的视图也被实例化了的时候调用。在这个函数里,我们可以进行一些状态的恢复,加载数据等操作。当然,这里可以调用

setRetainInstance(boolean)来保存他们的实例。这个函数的调用时期:onCreateView()之前,

onViewStateRestored()之后。

接着几个生命周期方法对应着Activity的生命周期。就不叙述了。

onDestoryView()

上面提过,和onCreateView相对应,当onCreateView方法已经返回一个视图,那么在释放资源时,将调用该方法。

onDetach(Activity activity)

与onAttach相对应,当Fragment从宿主上面移除时调用。

注意:除了onCreateView方法外,其它方法重写的话必须调用父类对该方法的实现,也就是super()。

二、Fragment的使用

1、静态使用

继承Fragment,在onCreateView中初始化视图

在Activity的布局中使用fragment 布局,并且添加name属性,属性的值为第一步创建的Fragment类。

就这样两步就实现了。

2、动态使用

获取FragmentManager对象

调用FragmentManager的beginTransaction()方法,获取FragmentTransaction 对象

创建一个继承了Fragment对象的类

调用FragmentTransaction 的replace()方法

调用 FragmentTransaction 的commit()

3、Fragment的回退栈

类似与Activity,Fragment也有自己的回退栈,当我们把一个Fragment放入栈中,这个Fragment实例并不会被销毁,但是界面会被销毁,也就会调用onDestoryView()方法,当我们从另一个Fragment回退到放入回退栈中的Fragment时,会调用onCreateView()方法。

三、常用API

在上面动态使用中涉及了几个常用的API,还有未介绍的API

FragmentTransaction 类

transaction.add() :往一个Activity中添加一个Fragment

transaction.remove() :如果这个Fragment没有被添加到回退站,则这个Fragment则会被销毁,也就是调用onDestoryView()方法。

transaction.replace():remove()和add()合体。

addToBackStack(null):将当前Fragment添加到回退栈

transaction.hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

transaction.show():显示之前隐藏的Fragment

transatcion.commit():提交一个事务,注意:一定要在Activity.onSaveInstance()之前调用,否则会报State loss这样的错误。

Fragment类

detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach():重建view视图,附加到UI上并显示。

四、和ViewPager结合使用

Fragment和ViewPager结合使用的情况还是比较多大。将每一个Fragment作为ViewPager的一页。我们不用操作事务,利用adapter帮助我们完成。

1、FragmentPagerAdapter

FragmentPagerAdapter 继承自 PagerAdapter。相比通用的 PagerAdapter,该类更专注于每一页均为 Fragment 的情况。这个adapter每生成一个Fragment都会报错在内存中,因此适合页面比较少的情况。使用该类需要重写两个方法

getItem()

这个函数在instantiateItem()函数中被调用。看看源码。

@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}

final long itemId = getItemId(position);

// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}

return fragment;
}


首先判断事务是否为null,如果为空,则开启事务,接着调用getItemId(position)方法获取对应位置的itemid,通过itemid获取Fragment绑定的name,再获取Fragment。

然后判断Fragment是否为null,如果不为null,则调用事务的attach()方法,如果为null,则调用getItem()返回一个Fragment。从这里可以看出并不是每次创建都会创建Fragment,也就是说,不是每次都会调用getItem()方法,只有在第一次调用才会调用。这也是为什么有时候调用notifyDataSetChanged()不起作用的原因

getCount()

这个函数比较简单,只是单纯返回数量。

destroyItem() :该函数被调用后,会对 Fragment 进行 FragmentTransaction.detach()。Fragment 所占用的资源不会被释放。

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}


2、FragmentPagerStateAdapter

FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一样,是继承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一样的是,该 PagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面(就像 ListView 的实现一样)。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。

instantiateItem()源码:

@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do.  This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}

if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}

Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);

return fragment;
}


destroyItem() :将 Fragment 移除,即调用 FragmentTransaction.remove(),并释放其资源。

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;

if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);

mCurTransaction.remove(fragment);
}


五、横竖屏切换时的问题

解决这个问题之前我们先了解下当手机横竖屏切换时,Activity的生命周期方法的调用情况。

进入Activity

02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume


切换为横屏之后

02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onPause
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStop
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onDestroy
02-16 18:29:41.873 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume


从上面可以知道,当从竖屏切到横屏时,依次调用了onPause、onStop、onDestroy、onCreate、onStart、onResume。这就相当于重新启动了一个Activity。

OK、结合Fragment来看。

这里我贴上我的代码

Activity

public class ScreenActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen);
System.out.println("ScreenActivity:onCreate");
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.fl_content, new ScreenFragment("我是参数")).commit();
//            getSupportFragmentManager().beginTransaction().add(R.id.fl_content, ScreenFragment.newInstance("我是参数")).commit();
}

}

@Override
protected void onStart() {
super.onStart();
System.out.println("ScreenActivity:onStart");
}

@Override
protected void onResume() {
super.onResume();
System.out.println("ScreenActivity:onResume");
}

@Override
protected void onPause() {
super.onPause();
System.out.println("ScreenActivity:onPause");
}

@Override
protected void onStop() {
super.onStop();
System.out.println("ScreenActivity:onStop");
}

@Override
protected void onDestroy() {
super.onDestroy();
System.out.println("ScreenActivity:onDestroy");
}

@Override
protected void onRestart() {
super.onRestart();
System.out.println("ScreenActivity:onRestart");
}
}


Fragment

public class ScreenFragment extends Fragment {

private String arg = "无参构造器";

public ScreenFragment() {
System.out.println("ScreenFragment-----无参构造器");
}

public ScreenFragment(String arg) {
this.arg = arg;
}

public static ScreenFragment newInstance(String arg) {
Bundle bundle = new Bundle();
bundle.putString("arg", arg);
ScreenFragment fragment = new ScreenFragment();
fragment.setArguments(bundle);
return fragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_screen, container, false);
TextView tv = (TextView) view.findViewById(R.id.tv);
tv.setText(arg);
//        tv.setText(getArguments().getString("arg"));
return view;
}

}


如果安照上面的代码执行,则在从竖屏切换成为横屏时呈现无参构造器。解决方法:将Activity和Fragment两个地方的注释取消,并把注释上面的代码删除即可。

OK,关于Fragment就复习到这里了,以后要是又学到新东西再添加。

这篇博客所用到的代码: 源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Fragment 回退栈