可以用来做轮播,或者做fragment的滑动框架
一、简单的一个轮播
1.在布局文件layout里面写下viewpager控件
`<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>`
注意:viewpager是v4包里面的
2.ViewPager也和listview这些控件类似,需要适配器来加载view,写一个适配器
public class MyAdapter extends PagerAdapter {
private List<ImageView> imageViewList;
public MyAdapter(List<ImageView> imageViewList) {
this.imageViewList = imageViewList;
}
/**
* 获取viewpager的数目
* @return
*/
@Override
public int getCount() {
return imageViewList==null?0:imageViewList.size();
}
/**
*判断 返回的view和填充到viewpager的view是否一致 一致的才返回true
* @param view instantiateItem里面加入containerde的view
* @param object instantiateItem里面返回的值
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view==object;
}
/**
* 加载item ,要显示的内容加载出来
* @param container viewpager
* @param position 要加载的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
//获取当前要加载的图片
ImageView imageView=imageViewList.get(position);
container.addView(imageView);
return imageView;
}
/**
* 销毁view
* @param container viewpager对象
* @param position 当前位置
* @param object 将要销毁的对象view
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//从viewpager中移除view
container.removeView((View)object);
}
}
3.在activity里面加代码,和listview类似,获取数据源,传到适配器,设置适配器,下面代码,数据源为三张图片,已经加在资源文件中了,资源文件也可以是从网上获取
public class MainActivity extends AppCompatActivity {
//图片id数组
private int[] ids={R.mipmap.pic1,R.mipmap.pic2,R.mipmap.pic3,R.mipmap.pic4};
private ViewPager viewPager;
private List<ImageView> imageViewList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取viewpager控件
viewPager = (ViewPager)findViewById(R.id.viewpager);
//获取图片,把图片装入集合中
imageViewList = new ArrayList<>();
//给viewpager添加图片
for(int i=0;i<ids.length;i++){
//将图片加载到图片控件,然后加到图片集合中
ImageView imageView=new ImageView(this);
imageView.setImageResource(ids[i]);
imageViewList.add(imageView);
}
//加载适配器
viewPager.setAdapter(new MyAdapter(imageViewList));
}
}
无限轮播
无限轮播需要注意的地方:会容易在移动view时崩溃,
崩溃的原因,是从右往左滑的时候是先移除后加载,而往回滑的时候是先加载后移除。
解决崩溃主要思路:
在适配器里面设置getcount时,将返回的值设置为Integer.MAX_VALUE;
public int getCount() {
return Integer.MAX_VALUE;
}
因为这个count代表的viewpager一共有多少页view,如果填实际的数据源,在循环无限的轮播的时候容易出问题,
所以设置一个很大的数来,来充当数目。
在加载view时要注意,判断当前获取的imageview是否有父容器,如果有,则需要移除父容器,没有则添加到viewpager,
public Object instantiateItem(ViewGroup container, int position) {
int rightPosition = position % imageViewList.size();//实际应该加入viewpager里面的第几个条目
ImageView imageView = imageViewList.get(rightPosition);
if (imageView.getParent() == null) {//如果获取到的imageView没有父容器就不添加到viewPager
container.addView(imageView);
} else {//如果有父容器,就改变标记
isBack = true;
}
return imageView;
}
在销毁view时也要进行判断,判断是否为往左滑动,如果是,则不需要销毁view
//销毁view
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (!isBack) {//不是往左边滑的,才移除
container.removeView((View) object);
}
isBack=false;
}
以上操作的原因:
ViewPager在滑动时,每次加载时除了当前显示的视图,还会加载两个视图,前一个和后一个,
第一次加载时,前一个没有,只加载后一个,也只有这一次是例外。
但是每次加载前后两个图片的方式又根据滑动方式分为两种:
1、当你向右边滑动时,加载顺序是:先销毁上一个,再去加载下一个的下一个
2、当你向左边滑动时,加载顺序是:先加载上一个的上一个,在去销毁下一个
因为以上两种加载方式,就会导致在只有3张图的时候,出现程序崩溃。
比如有0 1 2 三张图,当前显示的是2,上一个是1和下一个0已经加载好了。
此时向左滑动,显示1,先加载0,会发现0已经有父容器了,加载失败,程序崩溃....
在代码里面,当只有三张图的时候,可以再添加三张一样的图,这样就不会出现以上情况了、
代码如下:(包括了handler实现自动轮播,以及小白点)
public class Main2Activity extends AppCompatActivity {
private ViewPager viewPager2;
private List<ImageView> imageViewList;
private int[] ids = {R.mipmap.pic1, R.mipmap.pic2, R.mipmap.pic3};
private int lastPoint=0;//记录上一个小白点的位置
//实现自动轮播
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 111://定时轮播
viewPager2.setCurrentItem(viewPager2.getCurrentItem() + 1);//设置轮播为下一页
sendEmptyMessageDelayed(111, 2000); //延迟两秒轮播
break;
}
}
};
private LinearLayout pointLinearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
viewPager2 = (ViewPager) findViewById(R.id.viewpager2);
imageViewList = new ArrayList<>();
//用来放白点
pointLinearLayout=(LinearLayout)findViewById(R.id.pointLinearLayout);
//给list加imageview
createImageview(false);
//3张图以下,会因为viewpager不同方向滑动,加载方式不同而出现一些问题,所以此处可以判断一下,
// 如果只有三张或一下的图,则多加三张一样的图
if (imageViewList.size() <= 3) {
createImageview(true);
}
viewPager2.setAdapter(new MyAdapterInfinite(imageViewList));
//初始将viewpager设置在中间,这样才可以往左边滑动
//Integer.MAX_VALUE/2-Integer.MAX_VALUE/2%imageViewList.size() 这个不仅取到了中间,而且取到了第一张图
//Integer.MAX_VALUE/2%imageViewList.size() 表示 中间那个位置的图片是实际的第几张图片,然后减去数,就是第0张图片也就是第一张图片
viewPager2.setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % imageViewList.size());
handler.sendEmptyMessage(111);
//viewpager的页面监听
viewPager2.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
/**
* 当页面滑动的时候
*
* @param position 当前正在滑动的那一页下标
* @param positionOffset 滑动的偏移量 百分比 已经滑动了多少了
* @param positionOffsetPixels 滑动的偏移量(像素),当前总共滑动了多少像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
* 页面被选择时
* @param position 当前页面的位置
*/
@Override
public void onPageSelected(int position) {
//用当前位置去求模得到小白点的位置,这里取余应该用ids,不能用imageviewlist,
//因为图片有可能会有复制的
int pointPosition=position%ids.length;
//获取小白点,并设置值
pointLinearLayout.getChildAt(pointPosition).setEnabled(false);
//改变上一个小白点的状体
pointLinearLayout.getChildAt(lastPoint).setEnabled(true);
//设置lastpoint的值为当前页面小白点
lastPoint=pointPosition;
}
/**
* 页面滑动的状态发生变化的时候
*
* @param state 包含拖动1 SCROLL_STATE_DRAGGING,
* 惯性滚动2 SCROLL_STATE_SETTLING,
* 停止0SCROLL_STATE_IDLE
*/
@Override
public void onPageScrollStateChanged(int state) {
}
});
//viewpager的触摸监听
viewPager2.setOnTouchListener(new View.OnTouchListener() {
/**
* 当触摸的时候停止轮播
* @param view 当前的view页面
* @param motionEvent 事件类型,按下,松开
* @return
*/
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction()== MotionEvent.ACTION_DOWN){//按下
handler.removeMessages(111);//移除handler
}else if(motionEvent.getAction()==MotionEvent.ACTION_UP){
handler.sendEmptyMessageDelayed(111,2000);
}
return false;
}
});
}
//用来把图片加载到list
//使用isCopy来判断是否是复制的数据,即图片数量小于等于3的时候
private void createImageview(boolean isCopy) {
for (int i = 0; i < ids.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setImageResource(ids[i]);
imageViewList.add(imageView);
if (!isCopy) {//图片资源不是复制的,就生产小白点
View view = new View(this);
view.setBackgroundResource(R.drawable.selector);//设置背景色
if (i == 0) {//如果是第一个则设置为红色
view.setEnabled(false);
}
//将当前位置加入到tag,下次点击事件可以使用
view.setTag(i);
//加入小白点到线性布局
view.setOnClickListener(new View.OnClickListener() {
/**
* 小白点的点击监听
* @param view
*/
@Override
public void onClick(View view) {
//获取点击的小白点的位置
int pointposition=(int)view.getTag();
//获取当前点击小白点位置和上个小白点的位置差
int sub=pointposition-lastPoint;
//viewpager当前页面去加上差值,就是点击了小白点后要显示的页面
viewPager2.setCurrentItem(viewPager2.getCurrentItem()+sub);
}
});
LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(44,44);
layoutParams.rightMargin=10;
pointLinearLayout.addView(view,layoutParams);
}
}
}
}
除了上面的一种方法:还可以在适配器里面,每次加载图片都new一个出来,而不是去源数据中获取已经存好了的imageview。
代码如下:
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = new ImageView(container.getContext());
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setImageResource(ids[position%ids.length]);
container.addView(imageView);
return imageView;
}
但是这样特别浪费内存.建议用上面的一种
TabLayout
一种可以与viewpager结合使用,很暴力的控件,需要添加一个依赖,因为是另外一个包里面的
1.在xml布局文件里面写
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorHeight="10dp"
app:tabSelectedTextColor="#fff"
app:tabMode="scrollable"
android:background="@drawable/shape"
app:tabIndicatorColor="#0f0"
app:tabTextColor="#00f"
></android.support.design.widget.TabLayout>
属性:
tabIndicatorColor 下标指示器的颜色
tabIndicatorHeight 下标指示器的高度
tabTextColor tab 上面的文件的颜色
tabSelectedTextColor 被选中的 tab的上面的字的颜色
2.在代码里面,viewpager的加载如上面一致,tablayout控件的设置,需要将获取到的TabLayout控件与viewpager联动,
//将tablayout与viewpager设置联动
tabLayout.setupWithViewPager(viewPager);
但是有一个问题,设置联动之后,如果给tablayout的tab设置了tag,与哪个 viewpager 保持联动, 这行代码会导致所有的 tab被删除然后重新添加,所以上面的属性都会被回收。所以想要tag生效,必须在最后再添加一次tag。
3.想要tablayout的文字显示出来,需要在viewpager的adapter里面重写一个方法
@Override
public CharSequence getPageTitle(int position) {
return ss.get(position);
}
这个方法会把文字添加进去。
1.只是适配器不同了,viewpager里面放fragment时,适配器不是pageadapter,而是用FragmentStatePagerAdapter和FragmentPageAdapter里面重写的方法也很简单,只有两个。
2.在代码里面的实现很简单,
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private List<Fragment> fragmentList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取viewpager
viewPager = (ViewPager)findViewById(R.id.viewpager);
//创建fragment
fragmentList = new ArrayList<>();
MyFragment myFragment = new MyFragment();
MyFragment2 myFragment2 = new MyFragment2();
MyFragment3 myFragment3 = new MyFragment3();
MyFragment4 myFragment4 = new MyFragment4();
//添加数据源
fragmentList.add(myFragment);
fragmentList.add(myFragment2);
fragmentList.add(myFragment3);
fragmentList.add(myFragment4);
//给viewpager设置适配器
/*
FragmentManager fm=getSupportFragmentManager();
viewPager.setAdapter(new MyAdapter(fm,fragmentList));
*/
//另外一种适配器
viewPager.setAdapter(new MyAdapterFragmentState(getSupportFragmentManager(),fragmentList));
viewPager.setOffscreenPageLimit(2);//前后各加载2个
}
}
3.viewpager 里面可以设置前后加载图片的数量,setOffscreenPageLimit
viewPager.setOffscreenPageLimit(2);//前后各加载2个
好像最多可以设置为3,即前后加载3张,每次加载7张图