您的位置:首页 > 其它

ViewPager防止Fragment销毁以及取消Fragment的预加载

2016-09-28 14:57 579 查看

ViewPager简介

ViewPager不用多说,是v4包中的一个控件,可以用来实现屏幕间的切换。

<android.support.v4.view.ViewPager />

跟listview一样,需要使用适配器,PagerAdapter

PagerAdapter 必须重写的四个函数:

boolean isViewFromObject(View arg0, Object arg1)
int getCount()
void destroyItem(ViewGroup container, int position,Object object)
Object instantiateItem(ViewGroup container, int position)

ViewPager+Fragment配合使用

使用方法

普通实现可以通过PageAdapter中添加View来实现,android官方最推荐的一种实现方法却是使用fragment。可以使用如下两种adapter

FragmentPagerAdapter/FragmentStatePagerAdapter 都是继承自PagerAdapter,必须重写一下两个函数

getItem(int)
getCount()

存在的问题

1. 默认情况下,ViewPager会根据setOffscreenPageLimit()方法设置的大小,自动预加载

2. 还是根据setOffscreenPageLimit()方法设置的大小,会去销毁fragment视图

下面的图说明情况



滑动fragment1,此时会预加载fragment2,滑动到fragment2会预加载fragment3,但是滑动到fragment3,此时会调用fragment1的destroyview方法,销毁视图。当重新滑动到fragment1才会重新调用fragment1的oncreateview方法。注意此时并不会销毁实例,不会调用ondestroy方法

这样就存在两个问题

pagerview频繁切换,导致fragment1.fragment3在频繁的调用destroyview和oncreateview方法,重新创建视图。这样也浪费了大量的资源,用户体验不佳,虽然内存消耗比较低
因为切换到fragment1的时候,同时预加载了fragment2,如果此时fragment2也有大量的耗时网络请求要做,如果应用对启动反应速度比较敏感,所以此时做了多余的工作。能否把这些耗时的工作延迟加载,也是个问题

解决方案

1. 防止频繁的销毁视图,setOffscreenPageLimit(2)/或者重写PagerAdaper的destroyItem方法为空即可

setOffscreenPageLimit(2)

//Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.Pages beyond this limit will be recreated from the adapter when needed.You should keep this limit low, especially if your pages have
complex layouts. This setting defaults to 1.

大概意思就是说:

设置当前page左右两侧应该被保持的page数量,超过这个限制,page会被销毁重建(只是销毁视图),onDestroy-onCreateView,但不会执行onDestroy。尽量维持这个值小,特别是有复杂布局的时候,因为如果这个值很大,就会占用很多内存,如果只有3-4page的话,可以全部保持active,可以保持page切换的顺滑

这下很好理解了,默认情况下是1,所以当前fragment左右两侧,就会被保持1页pager,所以上述切换到fragment2并不会销毁任何视图,但是到fragment1,3会。这里注意这个值,是左右两侧能够维持的page,所以如果setOffscreenPageLimit(2),那么就不会频繁的销毁了

destroyItem()

//super.destroyItem(container, position, object); 注释掉调用父类方法即可

2. 取消预加载,可以fragment的setUserVisibleHint实现,具体实现参考代码示例

setUserVisibleHint

//Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.An app may set this to false to indicate that the fragment's UI is scrolled
out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.

大概意思就是:fragment对用户可见,isVisibleToUser为true,不可见isVisibleToUser为false。对应于viewpager,当前pager,非当前pager

代码示例

主界面

[java] view
plain copy

package com.example.viewpagertest;

public class MainActivity extends FragmentActivity implements OnClickListener {

private TextView mFstBtn;

private TextView mSndBtn;

private TextView mThdBtn;

private ViewPager mViewPager;

private ListFragmentPagerAdapter mPagerAdapter;

private List<Fragment> mFragments = new ArrayList<Fragment>();

private final int FIRST_FRAGMENT = 0;

private final int SECOND_FRAGMENT = 1;

private final int THIRD_FRAGMENT = 2;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initItem();

initViewPager();

}

/**

* 初始化按钮

*/

private void initItem() {

mFstBtn = (TextView) findViewById(R.id.first_item);

mFstBtn.setActivated(true);

mFstBtn.setOnClickListener(this);

mSndBtn = (TextView) findViewById(R.id.second_item);

mSndBtn.setOnClickListener(this);

mThdBtn = (TextView) findViewById(R.id.third_item);

mThdBtn.setOnClickListener(this);

}

/**

* 初始化ViewPager控件

*/

private void initViewPager() {

mViewPager = (ViewPager) findViewById(R.id.id_vp_viewpager);

//Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.

//Pages beyond this limit will be recreated from the adapter when needed.

//You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.

mViewPager.setOffscreenPageLimit(2);

//3个page,所以第一个page启动的时候,右侧的page会加载,中间第二个page选中的时候,不会做任何事情,因为左右两侧现在各有一个,第三个page选中的时候,销毁第一个page的视图,

//因为现在page3左侧有2个page了,必须销毁远的那个

//意思就是说设置当前page左右两侧应该被保持的page数量,超过这个限制,page会被销毁重建(只是销毁视图),onDestroy-onCreateView,但不会执行onDestroy

//尽量维持这个值小,特别是有复杂布局的时候,因为如果这个值很大,就会占用很多内存,如果只有3-4page的话,可以全部保持active,可以保持page切换的顺滑

//添加Fragment

mFragments.add(CustomListFragment.newInstance(FIRST_FRAGMENT));

mFragments.add(CustomListFragment.newInstance(SECOND_FRAGMENT));

mFragments.add(CustomListFragment.newInstance(THIRD_FRAGMENT));

//适配器

mPagerAdapter = new ListFragmentPagerAdapter(getSupportFragmentManager(), mFragments);

mViewPager.setAdapter(mPagerAdapter);

mViewPager.addOnPageChangeListener(onPageChangeListener);

}

private OnPageChangeListener onPageChangeListener = new OnPageChangeListener() {

@Override

public void onPageSelected(int position) {

//根据用户选中的按钮修改按钮样式

activeItem(position);

}

@Override

public void onPageScrolled(int arg0, float arg1, int arg2) {

}

@Override

public void onPageScrollStateChanged(int arg0) {

}

};

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.first_item:

activeItem(0);

mViewPager.setCurrentItem(FIRST_FRAGMENT);

break;

case R.id.second_item:

activeItem(1);

mViewPager.setCurrentItem(SECOND_FRAGMENT);

break;

case R.id.third_item:

activeItem(2);

mViewPager.setCurrentItem(THIRD_FRAGMENT);

break;

}

}

private void activeItem(int item) {

switch (item) {

case 0:

mFstBtn.setActivated(true);

mSndBtn.setActivated(false);

mThdBtn.setActivated(false);

break;

case 1:

mFstBtn.setActivated(false);

mSndBtn.setActivated(true);

mThdBtn.setActivated(false);

break;

case 2:

mFstBtn.setActivated(false);

mSndBtn.setActivated(false);

mThdBtn.setActivated(true);

break;

default:

break;

}

}

}

界面布局

[java] view
plain copy

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical" >

<LinearLayout

android:layout_width="match_parent"

android:layout_height="45dp"

android:layout_margin="@dimen/padding_10"

android:background="@drawable/tab_corner_btn_all_selector"

android:orientation="horizontal" >

<TextView

android:id="@+id/first_item"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:background="@drawable/tab_corner_btn_left_selector"

android:gravity="center"

android:text="第一个"

android:textColor="@color/black"

android:textSize="15sp" />

<View

android:layout_width="@dimen/line"

android:layout_height="match_parent"

android:background="@color/orange" />

<TextView

android:id="@+id/second_item"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:background="@drawable/tab_corner_btn_center_selector"

android:gravity="center"

android:text="第二个"

android:textColor="@color/black"

android:textSize="15sp" />

<View

android:layout_width="@dimen/line"

android:layout_height="match_parent"

android:background="@color/orange" />

<TextView

android:id="@+id/third_item"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:background="@drawable/tab_corner_btn_right_selector"

android:gravity="center"

android:text="第三个"

android:textColor="@color/black"

android:textSize="15sp" />

</LinearLayout>

<android.support.v4.view.ViewPager

android:id="@+id/id_vp_viewpager"

android:layout_width="match_parent"

android:layout_height="match_parent" />

</LinearLayout>

基类的Fragment

[java] view
plain copy

package com.example.viewpagertest;

import android.support.v4.app.Fragment;

public abstract class BaseFragment extends Fragment {

/** Fragment当前状态是否可见 */

protected boolean isVisible;

//setUserVisibleHint adapter中的每个fragment切换的时候都会被调用,如果是切换到当前页,那么isVisibleToUser==true,否则为false

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

super.setUserVisibleHint(isVisibleToUser);

if(isVisibleToUser) {

isVisible = true;

onVisible();

} else {

isVisible = false;

onInvisible();

}

}

/**

* 可见

*/

protected void onVisible() {

lazyLoad();

}

/**

* 不可见

*/

protected void onInvisible() {

}

/**

* 延迟加载

* 子类必须重写此方法

*/

protected abstract void lazyLoad();

}

具体fragment

[java] view
plain copy

package com.example.viewpagertest;

public class CustomListFragment extends BaseFragment {

private Context context;

private static final String FRAGMENT_INDEX = "fragment_index";

private final int FIRST_FRAGMENT = 0;

private final int SECOND_FRAGMENT = 1;

private final int THIRD_FRAGMENT = 2;

private TextView contentText;

private ProgressBar progressBar;

private int mCurIndex = -1;

/**

* 标志位,标志已经初始化完成

*/

private boolean isPrepared;

/**

* 是否已被加载过一次,第二次就不再去请求数据了

*/

private boolean mHasLoadedOnce;

/**

* 创建新实例

*

* @param index

* @return

*/

public static CustomListFragment newInstance(int index) {

Bundle bundle = new Bundle();

bundle.putInt(FRAGMENT_INDEX, index);

CustomListFragment fragment = new CustomListFragment();

fragment.setArguments(bundle);

return fragment;

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

context = getActivity();

Bundle bundle = getArguments();

if (bundle != null) {

mCurIndex = bundle.getInt(FRAGMENT_INDEX);

}

}

@Override

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

View view = inflater.inflate(R.layout.fragment, container, false);

contentText = (TextView) view.findViewById(R.id.content);

progressBar = (ProgressBar) view.findViewById(R.id.progressbar);

isPrepared = true;

lazyLoad();

return view;

}

@Override

protected void lazyLoad() {

if (!isPrepared || !isVisible || mHasLoadedOnce) {

return;

}

new AsyncTask<Void, Void, Boolean>() {

@Override

protected void onPreExecute() {

super.onPreExecute();

progressBar.setVisibility(View.VISIBLE);

}

@Override

protected Boolean doInBackground(Void... params) {

try {

Thread.sleep(2000);

//在这里添加调用接口获取数据的代码

//doSomething()

} catch (Exception e) {

e.printStackTrace();

}

return true;

}

@Override

protected void onPostExecute(Boolean isSuccess) {

if (isSuccess) {

// 加载成功

setView();

mHasLoadedOnce = true;

} else {

// 加载失败

}

progressBar.setVisibility(View.GONE);

}

}.execute();

}

private void setView() {

// 根据索引加载不同视图

switch (mCurIndex) {

case FIRST_FRAGMENT:

contentText.setText("第一个");

break;

case SECOND_FRAGMENT:

contentText.setText("第二个");

break;

case THIRD_FRAGMENT:

contentText.setText("第三个");

break;

}

}

@Override

public void onDestroyView() {

super.onDestroyView();

Log.d("LiaBin", "onDestroyView: curIndex=" + mCurIndex);

}

@Override

public void onDestroy() {

super.onDestroy();

Log.d("LiaBin", "onDestroy: curIndex=" + mCurIndex);

}

}

FragmentPagerAdapter实现

[java] view
plain copy

package com.example.viewpagertest;

public class ListFragmentPagerAdapter extends FragmentPagerAdapter {

private static final int TAB_COUNT = 3;

private List<Fragment> mFragmentList;

public ListFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {

super(fm);

this.mFragmentList = fragments;

}

@Override

public Fragment getItem(int position) {

return (mFragmentList == null || mFragmentList.size() < TAB_COUNT) ? null : mFragmentList.get(position);

}

@Override

public int getCount() {

return mFragmentList == null ? 0 : mFragmentList.size();

}

//Remove a page for the given position. The adapter is responsible for removing the view from its container

//防止重新销毁视图

@Override

public void destroyItem(ViewGroup container, int position, Object object) {

//如果注释这行,那么不管怎么切换,page都不会被销毁

super.destroyItem(container, position, object);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息