V4包-继承viewgroup
http://www.jianshu.com/p/e5abbda4a71c
setOffscreenPageLimit(0)
在空闲状态下,设置应保留在视图层次结构中当前页面任一侧的页数。 超出此限制的页面将在需要时从适配器重新创建。
如果您事先知道需要支持的页面数量,或者在您的页面上放置了延迟加载机制,则调整此设置可以在分页动画和交互的感知平滑度方面带来好处。 如果您有一小部分页面(3-4)可以一次保持活动状态,那么当用户来回翻页时,新创建的视图子树的布局花费的时间会更少。</ p>
<p>您应该保持低限,特别是如果您的页面布局复杂。此设置默认为1。
setCurrentItem
设置当前选择的页面。 如果ViewPager已经通过其当前适配器的第一个布局,那么在当前项目和指定项目之间将会有平滑的动画过渡。
使用
Adapter
class VpAdapter extends PagerAdapter {
//重写getCount--要展示几个条目
// 返回viewpager一共要展示几个条目
@Override
public int getCount() {
return vpMax;
}
//重写isViewFromObject
/**
* object 也是我们在instantiateItem 返回的iv(如果返回的是view按照这个去显示,如果返回的是其他view按照其他策略去显示)
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//重写instantiateItem—如同getview
/**
* 类似 我们之前学的getView方法 通过这个方法添加viewpager要展示每个条目参1:container :就代表viewpager
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
// 0对Position对%5
position = position % vpLists.size();
//1 通过Position 找到我们要展示的条目
ImageView iv = vpLists.get(position);
//2 需要把iv手动加入到容器中
container.addView(iv);
//return iv
return iv;
}
//重写destroyItem--用不到的条目销毁—要求被销毁
/**
* 把用不到的条目销毁 object 实际上就是我们在instantiateItem这个方法返回的对象
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
无限循环
实际上是返回一个较大的数
int vpMax = 1000000 * imageResIds.length;
position = position % vpLists.size();
viewpager默认加载3页,因此要保证初始数据要大于3页,这个无限循环才可用。
自动轮播
//Handler-4s发送消息sendEmptyMessageDelayed -- setCurrentItem(vp.getCurrentItem() + 1)
//创建handler
// 创建一个handler
private Handler handler = new Handler() {
//写出handlemassage方法—处理消息
public void handleMessage(android.os.Message msg) {
//显示viewpager的下一个条目
vp.setCurrentItem(vp.getCurrentItem() + 1);
//无线循环发送—自己发送消息
handler.sendEmptyMessageDelayed(100, 4000);
}
};
//可见的时候发送消息-
@Override
protected void onStart() {
// 4秒钟发送消息
//参1:代表发消息的类型 参2:时间
handler.sendEmptyMessageDelayed(100, 4000);
super.onStart();
}
//当用户按home键后—不需要再运行无限循环—移除消息
@Override
protected void onStop() {
handler.removeMessages(100);
super.onStop();
}
用户滑动时,viewpager不进行自动轮播
//用户滑动时—不让轮播图自己滑动
// 一个控件处理触摸事件—两种方式写一个控件—覆盖ontouchevent所有 的view都让他有setontouchlistener
//监听Imageview,处理触摸事件
imageView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
//按下事件处理
case MotionEvent.ACTION_DOWN://viewgroup--默认都会传递一个down事件
//用户手指按下,停止轮播图播放
// 把Handler中的消息删除
myHandler.removeCallbacksAndMessages(null);
break;
//抬起事件处理
case MotionEvent.ACTION_UP:
//用户手指抬起,继续轮播图播放
System.out.println("用户手指抬起,继续轮播图播放");
myHandler.sendEmptyMessageDelayed(0, 3000);
break;
//处理cancel事件
case MotionEvent.ACTION_CANCEL:
// 在ImageView上滑动时,move事件被父容器ViewPager处理了,父容器不给传递move、up事件,传递下来cancel事件
//用户手指移动,继续轮播图播放
myHandler.sendEmptyMessageDelayed(0, 3000);
break;
default:
break;
}
return true;
// 如果自己没有消费,父容器不再把剩下的事件传递下来
//如果在按下后--return false--没有消费事件
//则父容器不会再把剩下的事件(移动,抬起)等传递进来
}
}
);
滑动监听
vp.addOnPageChangeListener(new OnPageChangeListener() {
//当一个新的页面被选中的时候执行
@Override
public void onPageSelected(int position) {
}
//当页面开始滚动 的时候开始执行
//参数1 当前条目的索引,
//参数2 ViewPager滑动的距离/ViewPager的宽度
//参数3 ViewPager滑动的距离
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//当页面状态发生改变的时候执行
@Override
public void onPageScrollStateChanged(int state) {
}
});
跳转到对应索引页面
//点击那张图片让其显示哪张图片
viewPager.setCurrentItem(current);
ViewPagerIndicatorLibrary指示器第三方库
使用—下载demo—查看例子—看打开的是哪个页面—到对应页面找到对应布局拷贝过来
布局
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/indicator"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
绑定指示器
indicator = (TabPageIndicator) view.findViewById(R.id.indicator);
pager = (ViewPager) view.findViewById(R.id.pager);
//更新ViewPager
pager.setAdapter(new MyAdapter());
//绑定ViewPager标题指示器—在设置adapter以后—(没有返回内容时 能点击但是看不到内容)
indicator.setViewPager(pager);
在adapter中返回对应标题
//返回ViewPager对应位置的标题内容
@Override
public CharSequence getPageTitle(int position) {
return data.get(position).title;
}
更改标题样式
Demo中的activity的代码和布局中都没有修改其控件颜色,到demo的清单文件中去查找对应的主题
将其复制出来—用上
然后更布局的背景颜色
更改标题背景及文字selector
更改背景图片
1拷贝图片到原项目中
修改文字选择效果-同修改背景图片
拷贝一份,背景图片的indicator—改名
修改值—drawablecolor
添加引用—在样式中
小圆点指示器circlepageindicator
轮播图圆点指示器—samplecirclesdefault—小圆点有点击事件
布局
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<cn.itheima.zhbj99.view.TopImageViewPager
android:id="@+id/vp_topimage"
android:layout_width="match_parent"
android:layout_height="200dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#66000000">
<TextView
android:id="@+id/tv_newsitem_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="标题"
android:textColor="#fff"
android:textSize="15sp" />
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/indicator"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:padding="5dp"
app:fillColor="#f00"
app:pageColor="#fff"
app:radius="3dp"
app:strokeWidth="0dp" />
</RelativeLayout>
</RelativeLayout>
关联圆点指示器
//找到控件
indicator = (CirclePageIndicator) view.findViewById(R.id.indicator);
// 关联圆点指示器
indicator.setViewPager(vp_topimage);
设置圆点移动时不需要圆点移动过程
indicator.setSnap(true);
修改圆点属性
可以在layout里面修改颜色
可以在方法中改
可以在样式中改
找到samplecirclestylelayout—这个类
点击进入布局
xmlns:app="http://schemas.android.com/apk/res-auto"
<com.viewpagerindicator.CirclePageIndicator
android:id="@+id/indicator"
android:layout_width="60dp"
android:layout_height="wrap_content"
拷贝过自定义属性后再设置去右边的
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:padding="5dp"
里面移动的颜色
app:fillColor="#f00"
背景白色
app:pageColor="#fff"
半径—小一点
app:radius="3dp"
边框—0dp无边框
app:strokeWidth="0dp" />
TabLayout与ViewPager关联
布局中设置
app:tabIndicatorColor="#00f" //横线颜色
app:tabSelectedTextColor="#f00" //选中字体颜色
app:tabTextColor="#0f0"//默认字体颜色
app:tabMode="fixed"//fixed:不能滑动,每个Tab平分宽度,scrollable:可以滑动tab,每个tab宽度很小,适用于tab很多的情景
app:tabGravity="fill"//fill:平分宽度,center:让tab居中
关联tablayout
//先给ViewPager填充数据,然后关联TabLayout和ViewPager:
//给ViewPager填充数据
viewpager.setAdapter(new MyAdapter());
//关联TabLayout和ViewPager
tabLayout.setupWithViewPager(viewpager);
adapter返回标题
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
ViewPager动画
public class RotatePageTransformer implements PageTransformer {
private int degress = 25;
@Override
public void transformPage(View page, float position) {
int width = page.getWidth();
int height = page.getHeight();
// page ViewPager的条目 当前条目和预加载的条目
// position 条目移动的位置
if (position < -1) {// <-1 条目划出预加载长度,被viewpager删除
page.setRotation(0);
} else if (position < 0) {// -1~0 当前显示的条目滑到左边,或者左相邻条目滑动到显示
page.setRotation(position * degress);
// 设置旋转的中心点为 控件下边中心
page.setPivotX(width / 2);
page.setPivotY(height);
} else if (position <= 1) {// 0~1 当前显示的条目滑到右边,或者右相邻条目滑动到显示
page.setRotation(position * degress);
// 设置旋转的中心点为 控件下边中心
page.setPivotX(width / 2);
page.setPivotY(height);
} else {// > 1 条目划出预加载长度,被viewpager删除
page.setRotation(0);
}
}
}
ViewPager设置旋转动画
// 参数1 默认ViewPager绘制条目时,是从0到末尾
//true翻转绘制条目顺序 从末尾到0绘制,
//false 从0到末尾绘制
//就是在adapter中addview的顺序
vp_viewpager.setPageTransformer(true, new RotatePageTransformer());
第三方库添加动画
添加依赖
compile 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32@aar'
//给ViewPager设置动画效果--第三方库viewpagertransforms
//3D效果
viewPager.setPageTransformer(true, new CubeOutTransformer());
包含fragment
布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
app:layout_scrollFlags="scroll"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
app:tabIndicatorColor="@android:color/white"
app:tabIndicatorHeight="3dp"
app:tabMode="scrollable"
app:tabSelectedTextColor="@android:color/white"
app:tabTextAppearance="@style/MainTabTextStyle"
app:tabTextColor="#8fff" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
初始化数据
/**
* 初始化数据
*/
private void initData() {
//准备数据
list.add(new aaFragment());
list.add(new bbFragment());
list.add(new ccFragment());
list.add(new ddFragment());
//获取字符串数组资源
String[] titles = getResources().getStringArray(R.array.tab_names);
//给ViewPager填充数据
MainAdapter mainAdapter = new MainAdapter(getSupportFragmentManager(), list, titles);
viewPager.setAdapter(mainAdapter);
//绑定TabLayout和ViewPager
tabLayout.setupWithViewPager(viewPager);
}
adapter
public class MainAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> list;
String[] titles;
public MainAdapter(FragmentManager fm,ArrayList<Fragment> list,String[] titles) {
super(fm);
this.list = list;
this.titles = titles;
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
@Override
public int getCount() {
return list.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
}
处理fragment缓存问题
public abstract class BaseFragment extends Fragment implements StateLayout.OnReloadListener{
StateLayout stateLayout;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//在FragmentPagerAdapter中,当滑到第3页的时候,并没有销毁Fragment对象,
//只是将Fragment的View从ViewPager中移除了,因此当我们重新进入Fragment的onCreateView
//方法的时候,那个stateLayout变量就不再是空了。
if(stateLayout==null){
// LogUtil.e(this.getClass().getSimpleName()+" stateLayout==null: "+(stateLayout==null));
stateLayout = new StateLayout(getContext());
stateLayout.setSuccessView(getSuccessView());
stateLayout.showLoading();
stateLayout.setOnReloadListener(this);
loadData();
}
return stateLayout;
}
}
包含imageView
设置adapter,展示数据
vp_topimage.setAdapter(new MyAdapter());
adapter
class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return topnewsData.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//新建imageview
//ImageView imageView = new ImageView(context);
//内部使用 imageView.setImageResource() 相对于设置src(按照图片的比例进行缩放)
//设置background按照view的大小进行填充
// 为了让ImageView的图片宽高填满,需要设置图片的缩放类型
//imageView.setScaleType(ScaleType.FIT_XY); 图片按照控件的大小缩放,比例会变化
//设置图片的缩放类型
imageView.setScaleType(ScaleType.CENTER_CROP); //图片按照控件的比例缩放,超出的图片剪切掉
//使用Glide 图片处理库,下载并且展示图片—导jar包—load(url).into(展示的控件)
好处:
//本地中有会去本地取, 在内存中会去内存存取, 内存和本地都没有才去访问网络
Glide.with(context).load(topnewsData.get(position).topimage).into(imageView);
//将imageview添加到viewpager
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
tablayout添加+号
布局—+号—从上往下解析xml的
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.viewpagerindicator.TabPageIndicator
android:id="@+id/indicator"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1" />
<ImageButton
android:id="@+id/ib_itemselect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="3dp"
android:background="@drawable/item_select" />
</LinearLayout>
viewpager嵌套滑动事件冲突解决
在左右滑动的时候--事件被抢—导致图片显示不全
里面是小的viewpager—外面是大的viewpager—导致事件有可能被抢
请求父容器(viewpager)不要拦截你的事件
1、只处理左右滑动 (不处理上下滑动)
1.1 第一页,且手指从左往右,自己不处理,让父容器拦截
1.2 最后一页,且手指从右往左,自己不处理,让父容器拦截
1.3其他情况自己处理事件
2、不处理上下滑动
重新继承viewpager,重写dispatch
public class TopImageViewPager extends ViewPager {
private int downX;
private int downY;
public TopImageViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
//判断分析
//1、只处理左右滑动 (不处理上下滑动)
//1.1 第一页,且手指从左往右,自己不处理,让父容器拦截
//1.2 最后一页,且手指从右往左,自己不处理,让父容器拦截
//1.3其他情况自己处理事件
//2、不处理上下滑动
//重写分发事件dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 请求父容器不拦截自己的事件
// getParent().requestDisallowInterceptTouchEvent(true);// true 是不拦截
// false 是拦截
//获取xy—判断是左右滑动
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: //按下
downX = (int) ev.getX();
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE: //移动
int moveX = (int) ev.getX();
int moveY = (int) ev.getY();
int diffX = moveX - downX;
int diffY = moveY - downY;
//1、只处理左右滑动
if (Math.abs(diffX) > Math.abs(diffY)) {
//1.1 第一页,且手指从左往右,自己不处理,让父容器拦截
if (getCurrentItem() == 0 && diffX > 0) {
//请求父容器拦截自己的事件- true 是不拦截-- false 是拦截
getParent().requestDisallowInterceptTouchEvent(false);
}
//1.2 最后一页,且手指从右往左,自己不处理,让父容器拦截
else if (getCurrentItem() == getAdapter().getCount() - 1
&& diffX < 0) {
请求父容器拦截自己的事件- true 是不拦截-- false 是拦截
getParent().requestDisallowInterceptTouchEvent(false);
}
//1.3 其他情况自己处理事件
else {
请求父容器不拦截自己的事件- true 是不拦截-- false 是拦截
getParent().requestDisallowInterceptTouchEvent(true);
}
} else {
//2、不处理上下滑动
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
}
不滚动的viewpager
public class NoScrollViewPager extends ViewPager {
public NoScrollViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoScrollViewPager(Context context) {
super(context);
}
// 事件拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;// 不拦截子控件的事件
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// 重写此方法, 触摸时什么都不做, 从而实现对滑动事件的禁用
return true;
}
}
布局
<android.support.design.widget.TabLayout
android:id="@+id/tl_personal"
style="@style/CustomTab"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_height" />
<com.widget.NoScrollViewPager
android:id="@+id/vp_personal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
设置viewpager
//先给ViewPager填充数据,然后关联TabLayout和ViewPager:
//给ViewPager填充数据
mVp.setAdapter(new PersonalDataVpAdapter(getSupportFragmentManager(), mTab_fragments, mTab_titles));
mVp.setOffscreenPageLimit(1);
//关联TabLayout和ViewPager
mTab.setupWithViewPager(mVp);
adapter
public class PersonalDataVpAdapter extends FragmentPagerAdapter {
private BaseFragment[] mTab_fragments;
private String[] mTab_titles;
public PersonalDataVpAdapter(FragmentManager fm, BaseFragment[] mTab_fragments, String[] mTab_titles) {
super(fm);
this.mTab_fragments = mTab_fragments;
this.mTab_titles = mTab_titles;
}
@Override
public Fragment getItem(int position) {
return mTab_fragments[position];
}
@Override
public int getCount() {
return mTab_fragments.length;
}
@Override
public CharSequence getPageTitle(int position) {
return mTab_titles[position];
}
}