现在很多浏览类型app都是viewpager + fragment + 自定义tab的组合,比如网易新闻,优酷等。
使用这种组合的好处: 可以在一个Activity中处理多个页面,方便用户操作,视图结构清晰。
一、 viewpager 对应的几个adapter
PagerAdapter: 一般适配器,图片,view,Fragment都行。
FragmentPagerAdapter: 为PageAdapter子类,
适用于数量较少的Fragment为页面。特点,每一个生成的Fragment都将保存到内存中,当不可见时,销毁Fragment的view,调用onDestroyView; 用户体验好
FragmentStatePagerAdapter: 为PageAdapter子类, 适用于处理很多页,数据动态性较大,占用内存较多的Fragment为页面。 特点,当不可见时,整个Fragment都会销毁,调用onDestroy 和 onDestroyView; 数据需重新加载
一般PageAdapter中4个方法:
1. public int getCount(): 返回当前有效视图的个数。
2. public boolean isViewFromObject(View view, Object object): 该函数用来判断instantiateItem(ViewGroup, int)函数所返回来的Key与一个页面视图是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个View)
3. public Object instantiateItem(ViewGroup container, int position): 这个函数的实现的功能是创建指定位置的页面视图。返回值:返回一个代表新增视图页面的Object(Key),这里没必要非要返回视图本身,也可以这个页面的其它容器。
4. public void destroyItem(ViewGroup container, int position, Object object): 该方法实现的功能是移除一个给定位置的页面。适配器有责任从容器中删除这个视图。这是为了确保在finishUpdate(viewGroup)返回时视图能够被移除。
5. public CharSequence getPageTitle(int position): 使用系统PagerTabStrip PagerTitleStrip 返回的tab字符串
二、ViewPager 滑动页面的监听,一般可用于对自定义tab进行动画处理
//viewPager.setOnPageChangeListener(myOnPageChangeListener); 被弃用。 下面区别是将listener放入集合中
viewPager.addOnPageChangeListener(myOnPageChangeListener);
private MyOnPageChangeListener myOnPageChangeListener = new MyOnPageChangeListener();
public class MyOnPageChangeListener implements ViewPager.OnPageChangeListener {
@Override //滑动的过程调用 位置 当前页面屏幕的百分比 当前页面偏移的像素位置
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.d("stormxz", "onPageScrolled 111");
}
@Override //当前页面的位置
public void onPageSelected(int position) {
Log.d("stormxz", "onPageScrolled 222");
}
@Override //状态 1开始 2正在滑动 0结束
public void onPageScrollStateChanged(int state) {
Log.d("stormxz", "onPageScrollStateChanged 333");
}
}
一般用来对自定义tabbar 进行位置,动画设置
三、3种adapter使用Fragment + viewPager
1. PagerAdapter
package com.example.stormxz.viewpagerdemo;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* Created by stormxz on 2017/9/6.
*/
public class MyViewPagerAdapter extends PagerAdapter {
private ArrayList<Fragment> fragments = new ArrayList<Fragment>();
private ArrayList<String> titles = new ArrayList<String>();
private FragmentManager fragmentManager = null;
public MyViewPagerAdapter(ArrayList<Fragment> viewArrayList, ArrayList<String> titlesList, FragmentManager fragmentManager) {
this.fragments = viewArrayList;
this.titles = titlesList;
this.fragmentManager = fragmentManager;
}
@Override
public int getCount() { //返回fragment 的个数
return fragments.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object; //判读当前View是否一致
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = fragments.get(position);
if (fragment != null) {
if (!fragment.isAdded()){ //判读当前fragment是否已经提交 ,如果没有,则创建事务,进行提交
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(fragment, fragment.getClass().getSimpleName());
fragmentTransaction.commitAllowingStateLoss();
/**
* 在用FragmentTransaction.commit()方法提交FragmentTransaction对象后 会在进程的主线程中,用异步的方式来执行。
* 如果想要立即执行这个等待中的操作,就要调用这个方法(只能在主线程中调用)。 要注意的是,所有的回调和相关的行为都会在这个调用中被执行完成,因此要仔细确认这个方法的调用位置。
*/
fragmentManager.executePendingTransactions();
}
if (fragment.getView() != null) {
container.addView(fragment.getView());
}
}
return fragment.getView();
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(fragments.get(position).getView()); //removeView
}
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position); //返回tab 文字信息
}
}
MainActivity.java 传递FragmentManager tabname Fragment集合
myViewPagerAdapter = new MyViewPagerAdapter(fragments, titles, getSupportFragmentManager());
viewPager.setAdapter(myViewPagerAdapter);
一般不建议使用PagerAdapter. 建议使用其子类FragmentPagerAdapter 以及 FragmentStatePagerAdapter
2. FragmentPagerAdapter 与 FragmentStatePagerAdapter
只需要重新2个方法即可package com.example.stormxz.viewpagerdemo;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import java.util.ArrayList;
/**
* Created by stormxz on 2017/9/11.
*/
public class MyFragmentAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragments = new ArrayList<Fragment>();
public MyFragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(myFragmentAdapter);
源码中已经实现
事务提交
判断fragment是否是当前的,是则设置为用户可见
1. 使用,将其作为子类View 放入ViewPager中
@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); //添加Fragment
return fragment;
}
事务提交
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
判断fragment是否是当前的,是则设置为用户可见
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
四、 系统PagerTabStrip PagerTitleStrip
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="368dp"
android:layout_height="495dp"
tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="8dp">
<android.support.v4.view.PagerTabStrip
android:id="@+id/pager_tab_strip"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v4.view.PagerTabStrip>
<!-- <android.support.v4.view.PagerTitleStrip
android:id="@+id/pager_title_strip"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v4.view.PagerTitleStrip>-->
</android.support.v4.view.ViewPager>
2. 在Adapter中传入tabname集合,并在不同位置进行提取
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
相同点:
(1)作为ViewPager内部控件进行使用,只需要重写adapter中的getPageTitle(return position的字符串)
(2) android:layout_gravity 属性设置为TOP或BOTTOM来将它显示在ViewPager的顶部或底部。
不同点: PagerTabStrip 支持点击tab进行跳转页面,有下划线; PagerTitleStrip 则无
主流app 不会用