viewpager+fragment在日常的开发中经常用到,viewpager为了提高用户进行左右切换时的流畅度,实现了一套预加载的功能,在默认情况下,viewpager会预加载一个页面(默认情况下为1,可以自己设置),即当你使用viewpager+fragment时,除了当前可见的第一个fragment会被加载之外,这个fragment后面的一个fragment也会被加载,这样用户进行切屏时就会更为流畅。但是在一些特殊情况下,我们可能不需要预加载的功能,我们更希望当fragment对用户可见时才进行加载或者是网络请求或者是特殊的操作(友盟统计页面访问情况等)。
这次的预期效果就是当fragment对用户可见时才进行网络请求,其中网络请求是通过AsyncTask实现的延迟操作。先看下效果图。
具体实现很简单,主要是通过fragment中setUserVisibleHint(boolean isVisibleToUser)方法进行当前fragment是否可见的判断。只有当此时的fragment对用户可见并且已经被创建才进行网络请求。在网络请求成功后加一个标示,防止来回切pagerview时进行重复的网络请求。下面看下源码。
public class MyFragment extends Fragment {
private boolean isVisible = false;//fragment 可见标示
private boolean isCreated = false;//fragment 已经被加载标示
private boolean hasLoaded = false;//fragment 已做过网络请求标示
private String contentStr;
private TextView content;
private LinearLayout layout;
public static final String KEY = "FRAGMENT_KEY";
private String TAG = "fragment";
public static MyFragment newInstance(String content) {//初始化fragment
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString(KEY, content);
fragment.setArguments(bundle);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(layout==null){
layout = (LinearLayout) inflater.inflate(R.layout.fragment, container, false);
content = (TextView) layout.findViewById(R.id.content);
Bundle bundle = getArguments();
if (bundle != null) {
contentStr = bundle.getString(KEY);
}
isCreated = true;
loading(" in onCreateView ");
Log.v(TAG, contentStr + " isCreated ");
return layout;
}else{
Log.v(TAG, contentStr + " layout != null ");
return layout;
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
isVisible = true;
} else {
isVisible = false;
}
loading(" in setUserVisibleHint ");
}
private void loading(String str) {
if (hasLoaded) {
return;
}
if (isCreated && isVisible) {//可见并且已经被创建时,做网络请求
UIHelper.showDialogForLoading(getActivity(), "正在加载...", true);
Log.v(TAG, contentStr + str + "--->loading ");
new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
if (aBoolean) {
hasLoaded = true;
content.setText(contentStr);
UIHelper.hideDialogForLoading();
}
}
}.execute();
}
}
}
我们在关键的地方打一下log,看下loading方法是在oncreatView中执行还是在setUserVisibleHint中执行。
在再看下MainActivity的源码。
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private ArrayList<MyFragment> fragments=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager= (ViewPager) findViewById(R.id.view_pager);
fragments.add(MyFragment.newInstance("first fragment"));
fragments.add(MyFragment.newInstance("second fragment"));
fragments.add(MyFragment.newInstance("third fragment"));
MyFragmentAdapter adapter=new MyFragmentAdapter(getSupportFragmentManager(),fragments);
viewPager.setAdapter(adapter);
}
class MyFragmentAdapter extends FragmentPagerAdapter{
private ArrayList<MyFragment> fragments;
public MyFragmentAdapter(FragmentManager fm,ArrayList<MyFragment> fragments) {
super(fm);
this.fragments=fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
}
下面看下log。这是第一次运行时的log。
first fragment in onCreateView --->loading
first fragment isCreated
second fragment isCreated
因为viewPager在默认情况下,会预加载一个页面。(可以通过setOffscreenPageLimit设置预加载页面数)。所以当只是第一个fragment对用户可见时,第二个fragment已经被加载好了。并且第一个fragment是在onCreateView 中执行的loading方法,原因是此时setUserVisibleHint方法在onCreateView 之前调用但是此时isCreated为false所以无法执行网络请求。
下面切到到第二个fragment,此时的log。
second fragment in setUserVisibleHint --->loading
third fragment isCreated
此时第二个fragment因为已经被加载好了,并且在setUserVisibleHint方法中得知以为可见状态,所以执行了loading方法。于此同时第三个fragemnt也已经被加载。下面切到第三个fragment。
third fragment in setUserVisibleHint --->loading
与之前同理,因为此前第三个fragment已经被加载,而且此时对用户可见,可以执行loading方法。
最后贴一下工具类的代码。
public class UIHelper {
private static Dialog mLoadingDialog;
public static void showDialogForLoading(Activity context, String msg, boolean cancelable) {
if(context==null){
return;
}
View view = LayoutInflater.from(context).inflate(R.layout.loading_dialog, null);
TextView loadingText = (TextView)view.findViewById(R.id.dialog_content);
loadingText.setText(msg);
mLoadingDialog = new Dialog(context, R.style.loading_dialog_style);
mLoadingDialog.setCancelable(cancelable);
mLoadingDialog.setContentView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
mLoadingDialog.show();
}
public static void hideDialogForLoading() {
if(mLoadingDialog != null && mLoadingDialog.isShowing()) {
mLoadingDialog.cancel();
}
}
}