HorizontalScrollView自定义轮播控件
最近由于项目需要,需要每屏显示一张半图片且滚动时从后半张开始滚动,查了很多资料发现没有符合自己项目的第三方于是就自己写了个简洁版的,相比较传统viewpager轮播控件,更简洁,不需要adapter方便使用。
- 首先需要自定义个view继承HorizontalScrollView;
- 接下来就是在自定义的view中添加子控件了;
private View addView(final int position) {
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(paramsImage);
imageView.setImageResource(list.get(position));
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onChildrenClickListener.onImageClickListener(position);
}
});
return imageView;
}
由于项目只需要图片轮播,所以只添加了图片,如果需要其他的布局各位可以在这个方法中添加直接的布局。
注意:由于图片尺寸是服务器发来,需要动态计算,需要用的LayoutParams,至于怎么选需要看父布局,比如本项目中的布局是LinearLayout,就需要选LinearLayout.LayoutParams,具体的要具体问题具体分析。
- 子控件加完后,滚动比较好写了,用HorizontalScrollView自带的scrollTo和smoothScrollTo方法就好,这两个方法都能实现滚动的功能,唯一的区别是scrollTo滚动不带动画,smoothScrollTo滚动带动画,所以我选择了smoothScrollTo。
this.smoothScrollTo(count * scrollWidth, 0);
注意:count图片位置,scrollWidth你想要的滚动距离。
- 由于就需要让控件能滚动一定的指定距离,而不是 HorizontalScrollView的跟随手势滑动,所以需要重写dispatchTouchEvent方法来计算手指滑动了指定距离,并使控件滚动你想要的距离。由于备注比较详细就不啰嗦了,代码如下:
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: //手指按下ViewPageer时
//获得按下时XY轴的坐标
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_UP: //抬起触摸View的手指时
float diffx = ev.getX() - downX;
float diffy = ev.getY() - downY;
if (Math.abs(diffx) > Math.abs(diffy)) {
// 用户手指从左往右
// 如果是在第一个页面,手指从左往右,父容器响应
LogUtil.d("zqr", "水平滑动距离" + diffx);
if (diffx > 0) {
// 手指从左往右滑动,自己响应touch
if (diffx > MyUtils.getScreenWidth(context) / 3) {
handler.sendEmptyMessage(1);
LogUtil.d("zqr", "手指从左往右滑动,自己响应touch");
getParent().requestDisallowInterceptTouchEvent(false);
} else {
LogUtil.d("zqr", "手指从左往右滑动,子控件响应touch");
getParent().requestDisallowInterceptTouchEvent(true);
}
} else {
// 手指从右往左滑动,自己响应touch
if (diffx < -MyUtils.getScreenWidth(context) / 3) {
handler.sendEmptyMessage(0);
LogUtil.d("zqr", "手手指从右往左滑动,自己响应touch");
} else {
LogUtil.d("zqr", "手手指从右往左滑动,子控件响应touch");
}
}
} else {
// touch交给父容器
LogUtil.d("zqr", "垂直滑动大于水平滑动,父容器响应touch");
}
isPlay = true;
break;
case MotionEvent.ACTION_MOVE: //在View里面滑动手指时
LogUtil.d("zqr", "滑动" + MotionEvent.ACTION_MOVE);
break;
}
return super.dispatchTouchEvent(ev);
}
- 对你没看错我上面的代码了用到了handler,大部分童鞋肯定猜出来了,我是要用来实现自动播放功能,毕竟现在轮播都带自动播放功能,所以我们也得与时俱进嘛。
- 那么就来说说自动轮播的实现吧,实现方法有很多,不过我觉得用Timer来实现比较方便,其他的比如Thread什么的大家可以自己根据需求修改。Timer和TimerTask用完需要调用cancel()方法否则,会影响性能,代码如下:
private void timer() {
timer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
if (isPlay) {
handler.sendEmptyMessage(0);
}
}
};
timer.scheduleAtFixedRate(timerTask, time, time);
}
写到这基本上自动轮播的功能就实现了,因为一般轮播图片都需要监听点击事件,在这就有个小问题,只要手指滑动控件就会执行图片的监听事件,这就不是我想要的了,我们需要的是手指滑动一定距离图片换上一张或者下一张而不执行点击事件,只有在手指按下时才执行点击事件。所以还需要重写onTouchEvent方法,getParent().requestDisallowInterceptTouchEvent(true)返回true 就是本次触摸自己消耗掉响应,不传给子控件,代码如下:
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
LogUtil.d("zqr", "按下" + MotionEvent.ACTION_DOWN);
isPlay = false;
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
LogUtil.d("zqr", "按上" + MotionEvent.ACTION_UP);
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_CANCEL:
LogUtil.d("zqr", "取消" + MotionEvent.ACTION_CANCEL);
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
LogUtil.d("zqr", "滑动" + MotionEvent.ACTION_MOVE);
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return super.onInterceptTouchEvent(ev);
}
-
- 大家肯定会说,轮播没有指示器怎么可以,是的指示器肯定有啊,也是我自己写的,十分的简单,就是继承LinearLayout ,原理和轮播添加子控件差不多,在这就不多说了之后会有完整的项目上传的。
- 至此,基本上自定义半图轮播就完成了,而且这个控件的扩展性也不错,可以同屏显示2.5,3.5等等多种需求的轮播效果。
- 完整demo下载地址:http://download.csdn.net/download/zqrdy10/10139666 -
- 代码中肯定存在很多不足的地方,欢迎大家多多发表意见,可以私信我哦