前言
图片轮播是APP常见的功能,我这里就是用ViewPager实现了一个图片轮播的功能,它的不同之处:
1.实现自动轮播,只要是靠handler发送延时消息实现。
2.实现循环播放,只要原理:在List中0和Size位置分别添加尾部和头部信息,在ViewPager展示到position==0的时候,自动跳转到position==size的地方,而在ViewPager展示到position==size的时候,自动跳转到position==0的地方。
3.解决ViewPager的嵌套的冲突问题。
布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RadioGroup
android:id="@+id/radiogroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:orientation="horizontal" >
</RadioGroup>
</RelativeLayout>
ViewPager实现轮播,而RadioGroup利用了单一点击性完成图片下方提示点的作用。
点点的selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="true" android:drawable="@drawable/point_true"></item>
<item android:state_checked="false" android:drawable="@drawable/point_false"></item>
</selector>
主要原理
怕图片出现OOM,我这里用了xUtils中的BitmapUtils,这里可以根据实际情况选择。
ViewPager viewPager;
BitmapUtils bitmapUtils;
// 存放图片
List<ImageView> imageViews = new ArrayList<ImageView>();
// 适配器
ViewPagerAdapter mAdapter = new ViewPagerAdapter();
// 点点的父容器
RadioGroup radioGroup;
// 是否可循环
boolean recycled = true;
// 首页
int firstItem = 0;
// 循环展示的msg
static final int WHAT_RECYCLED = 0;
// 当前位置
int index;
String[] urls = {
"http://cdn.duitang.com/uploads/item/201409/13/20140913141520_Ydidj.jpeg",
"http://p.qq181.com/cms/1304/2013040607430286882.jpg",
"http://cdn.duitang.com/uploads/item/201409/17/20140917231336_URiHE.jpeg",
"http://img5.duitang.com/uploads/item/201409/13/20140913141545_E3xtA.thumb.700_0.jpeg",
"http://img4.duitang.com/uploads/item/201411/30/20141130160518_FzGfQ.jpeg",
"http://img1.imgtn.bdimg.com/it/u=744685945,4101336496&fm=21&gp=0.jpg",
"http://img4.duitang.com/uploads/item/201406/27/20140627004739_nQwxv.jpeg",
"http://img4.duitang.com/uploads/item/201206/06/20120606175027_BGuaY.thumb.700_0.jpeg" };
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
初始化,没什么好说的。
viewPager = (ViewPager) findViewById(R.id.viewpager);
radioGroup = (RadioGroup) findViewById(R.id.radiogroup);
bitmapUtils = new BitmapUtils(this);
// 添加存放图片的imageview,有几张图片生成几个imageview
for (int i = 0; i < urls.length; i++)
{
ImageView imageView = new ImageView(this);
bitmapUtils.display(imageView, urls[i]);
imageViews.add(imageView);
}
// 一张图片的时候不能循环
recycled = imageViews.size() >= 2;
// 为了可以循环滑动,在左右各加一个,最左边的界面是图片资源的最后一个,最右边的界面是图片资源的第一个
if (recycled)
{
ImageView imageView = new ImageView(this);
bitmapUtils.display(imageView, urls[urls.length - 1]);
imageViews.add(0, imageView);
ImageView imageView2 = new ImageView(this);
bitmapUtils.display(imageView2, urls[0]);
imageViews.add(imageView2);
firstItem = 1;
}
主要是根据图片的张数,生成对应的ImageView存放图片,为了防止OOM,用到了BitmapUtils,在左右两边再次添加首尾资源,主要有俩个原因:
1.第一次打开的时候,也可以左滑和右滑。
2.在使ViewPager即使在0和size位置的时候仍然可以滑动。这样我们才能监听滑动时间,使ViewPager再次跳转。(为什么有种网站跳转连接的感脚)
// 添加对应的点点图片
for (int i = 0; i < imageViews.size(); i++)
{
RadioButton rbtn = new RadioButton(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.rightMargin = 5;
params.leftMargin = 5;
rbtn.setLayoutParams(params);
rbtn.setBackgroundResource(R.drawable.point);
rbtn.setClickable(false);
radioGroup.addView(rbtn);
if (recycled && i == 0 || recycled && i == imageViews.size() - 1)
{
rbtn.setVisibility(View.GONE);
}
}
向RadioGroup中添加RadioButton,其实RadioButton就是用来显示的点点,注意的一点是第一个点和最后一个点要setVisibility(View.GONE)隐藏起来。
viewPager.setAdapter(mAdapter);
viewPager.setCurrentItem(firstItem);
viewPager.setOnPageChangeListener(listener);
index = viewPager.getCurrentItem();
((RadioButton) radioGroup.getChildAt(index)).setChecked(true);
mHandler.sendEmptyMessageDelayed(WHAT_RECYCLED, 3000);
添加监听,以及初始化。
class ViewPagerAdapter extends PagerAdapter
{
@Override
public int getCount()
{
return imageViews.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1)
{
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
container.removeView(imageViews.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position)
{
ImageView imageView = imageViews.get(position);
container.addView(imageView);
return imageView;
}
}
用过ViewPager的人应该都会写吧,没什么区别,不用详解了。
final OnPageChangeListener listener = new OnPageChangeListener()
{
@Override
public void onPageSelected(int position)
{
if (!recycled)
{
return;
}
if (position == 0)
{
index = imageViews.size() - 2;
viewPager.setCurrentItem(index);
} else if (position == imageViews.size() - 1)
{
index = 1;
viewPager.setCurrentItem(index);
} else
{
index = position;
}
((RadioButton) radioGroup.getChildAt(index)).setChecked(true);
}
@Override
public void onPageScrolled(int position, float arg1, int arg2)
{
}
@Override
public void onPageScrollStateChanged(int scrollState)
{
if (scrollState == ViewPager.SCROLL_STATE_DRAGGING)
{
mHandler.removeMessages(WHAT_RECYCLED);
mHandler.sendEmptyMessageDelayed(WHAT_RECYCLED, 3000);
}
}
};
添加页面切换事件。
1.onPageScrolled:主要处理滑动过程中的时间,position是当前位置,arg1滑动百分比,arg2滑动像素。
2.onPageScrollStateChanged:滑动状态改变,这里有三个状态,分别是ViewPager.SCROLL_STATE_IDLE(每个页面的初始化状态),ViewPager.SCROLL_STATE_DRAGGING(用户发起的滑动状态),ViewPager.SCROLL_STATE_SETTLING(页面切换的最终流程)。
3:onPageSelected:页面切换事件。
@SuppressLint("HandlerLeak")
final Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (recycled && msg.what == WHAT_RECYCLED)
{
index = viewPager.getCurrentItem() + 1;
viewPager.setCurrentItem(index);
sendEmptyMessageDelayed(WHAT_RECYCLED, 3000);
}
}
};
在这里我没3秒发送一次消息切换下一个页面,为了更好的用户体验,所以在onPageScrollStateChanged中用户操作的时候,取消消息的发送,在用户操作完成后再次发送。
当然,不要忘记在onPageSelected判断一下recycled,因为它标记了能否循环(在一张图片的时候肯定不能循环播放了)。然后就是在0和size位置跳转到n-1和1的位置。不要忘记radiobutton的状态啊~
好了,这样一个ViewPager循环滑动+自动滑动就实现了。
ViewPager 嵌套冲突
有的时候一个界面上会有俩个ViewPager,这样,可能我们滑动嵌套的ViewPager的时候,外面的ViewPager响应了滑动事件,所以,我们要判断一下滑动的位置来解决冲突。
public NestViewPager(Context context)
{
super(context);
}
public NestViewPager(Context context, AttributeSet attrs)
{
super(context, attrs);
}
不要忘记构造方法。。。不要忘记构造方法。。。不要忘记构造方法。。。重要的事说三遍,我还记得我第一次重写View的时候,不知道要重写构造方法,那个苦闷啊 - -。
public boolean onTouchEvent(MotionEvent evt)
{
switch (evt.getAction())
{
case MotionEvent.ACTION_DOWN:
// 记录按下时候的坐标
downPoint.x = evt.getX();
downPoint.y = evt.getY();
if (this.getChildCount() > 1)
{ // 有内容,多于1个时
// 通知其父控件,现在进行的是本控件的操作,不允许拦截
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_MOVE:
if (this.getChildCount() > 1)
{ // 有内容,多于1个时
// 通知其父控件,现在进行的是本控件的操作,不允许拦截
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
// 在up时判断是否按下和松手的坐标为一个点
if (PointF.length(evt.getX() - downPoint.x, evt.getY()
- downPoint.y) < (float) 5.0)
{
//onSingleTouch(this);
return true;
}
break;
}
return super.onTouchEvent(evt);
}
重写触屏事件,就是在本控件被触摸的时候,通知父控件不能拦截额。
public void onSingleTouch(View v)
{
if (onSingleTouchListener != null)
{
onSingleTouchListener.onSingleTouch(v);
}
}
public interface OnSingleTouchListener
{
public void onSingleTouch(View v);
}
public void setOnSingleTouchListener(
OnSingleTouchListener onSingleTouchListener)
{
this.onSingleTouchListener = onSingleTouchListener;
}
这里我还写了个接口,为的是,在点击本控件中的子控件的时候,通过回调完成响应的操作,当然看你的需求了。
最后~~~ 效果图: