android自定义控件-AutoScrollViewpager(无限滚动轮播控件)

在实现该控件之前,先说一下该控件的难度,

一、

  每个item中如果有RadioButton之类,可以focus焦点的,点击效果可能会失效 

 二、无限的滚动

  下面是效果图:


实现上图的效果,一共自定义了两个 控件,viewpager+底部导航图标

下面我先来讲解一下,viewpager的实现:

1.初始化

<pre name="code" class="java">/** 点击按下的坐标 **/
	PointF downP = new PointF();
	/** 当前按下的坐标 **/
	PointF curP = new PointF();
	OnSingleTouchListener onSingleTouchListener;
	public MyPagerAdapter adapter;
	public ArrayList<View> listViews;
	private Activity acitvity;
	private boolean isTouch = false;
	AutoScrollViewPager viewpager;
	AutoScrollViewPagerStateChange stateChange;

 
public AutoScrollViewPager(Context context, AttributeSet attrs) {
		super(context, attrs);
		listViews = new ArrayList<View>();
		viewpager = this;
		adapter = new MyPagerAdapter();
		viewpager.setAdapter(adapter);
		initOntouch();
	}<pre code_snippet_id="1780518" snippet_file_name="blog_20160723_3_7858134" name="code" class="java"><pre code_snippet_id="1780518" snippet_file_name="blog_20160723_2_1968790" name="code" class="java"><pre code_snippet_id="1780518" snippet_file_name="blog_20160723_3_7858134" name="code" class="java">listViews 存放轮播所需的图片
adapter是  viewpager的pageradapter
initOntouch()方法是设置touch事件的监听实现点击,获取当前点击的下标
通过对比touch中 down和up的点的x,y的值是否相同,相同表示点击,不同不做处理
 
 
 
private void initOntouch() {
		// TODO Auto-generated method stub
		viewpager.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View arg1, MotionEvent arg0) {
				// TODO Auto-generated method stub
				// 给当前坐标赋值
				curP.x = arg0.getX();
				curP.y = arg0.getY();

				if (arg0.getAction() == MotionEvent.ACTION_DOWN) {
					//
					// 给当前按下赋值
					downP.x = arg0.getX();
					downP.y = arg0.getY();
					// 设置 获取当前事件,通知父控件不将事件在进行分发
					getParent().requestDisallowInterceptTouchEvent(true);
					isTouch = true;
				}

				if (arg0.getAction() == MotionEvent.ACTION_MOVE) {
					//
					getParent().requestDisallowInterceptTouchEvent(true);
					isTouch = true;
				}

				if (arg0.getAction() == MotionEvent.ACTION_UP) {
					//
					//判断是否是点击操作
					<span style="color:#ff6666;">if (downP.x == curP.x && downP.y == curP.y) {
						onSingleTouch(viewpager.getCurrentItem()
								% listViews.size());
					}</span>
					isTouch = false;

				} else if (arg0.getAction() == MotionEvent.ACTION_CANCEL) {
					getParent().requestDisallowInterceptTouchEvent(false);

				}

				return false;
			}
		});
	}<pre name="code" class="java"><span style="white-space:pre">	</span>/**
	 * 单击事件
	 */
	public void onSingleTouch(Object obj) {
		if (onSingleTouchListener != null) {
			onSingleTouchListener.onSingleTouch(obj);
		}
	}

	/**
	 * 单机事件接口
	 */
	public interface OnSingleTouchListener {
		public void onSingleTouch(Object obj);
	}

 
</pre><pre code_snippet_id="1780518" snippet_file_name="blog_20160723_10_5295814" name="code" class="java">getParent().requestDisallowInterceptTouchEvent(false);该方法是通知父控件是否还要继续分发事件,true表示阻止父控件继续分发,false表示不做限制
<span style="font-family: Arial, Helvetica, sans-serif;">isTouch 表示再定時器中做处理</span>
2.无限轮播的原理 
<span style="color:#ff6666;">  原理: 假设  一共有  4张图片,当viewpager滚动到第4张得时候,图片直接显示第一张,所以在 viewpager的adapter中就要做如下处理</span>
// viewpager的 adapter
	private class MyPagerAdapter extends PagerAdapter {

		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			// return listViews.size();
			return Integer.MAX_VALUE;
		}

		@Override
		public boolean isViewFromObject(View arg0, Object arg1) {
			// TODO Auto-generated method stub
			return arg0 == arg1;
		}

		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView(listViews.get(position % listViews.size()));// 删除上个图片,如果不删除的话,当 viewpager调用 <span style="font-family: Arial, Helvetica, sans-serif;">instantiateItem 方法获取 </span><span style="font-family: Arial, Helvetica, sans-serif;">position % listViews.size() 的图片时,会报  该图片已被占用</span><span style="font-family: Arial, Helvetica, sans-serif;">
</span>
		}

		public Object instantiateItem(ViewGroup container, int position) {
			container.addView(listViews.get(position % listViews.size()), 0);//
			return listViews.get(position % listViews.size());
		}
	}
其中 设置 让 getCount返回一个 最大值,这样在用户浏览的时候,看起来是一直在做无限轮播,(ps:如果用户如果有足够的耐心等待,图片真正的轮播完全的时候,图片就会停留到  listViews.get(Integer.MAX_VALUE % listViews.size())的那张图片,不过得很长时间)
 
 
<span style="font-family: Arial, Helvetica, sans-serif;">3.定时器</span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">	/**
	 * 定时器
	 */
	private Timer time;
	boolean isScroll = false;

	public void startScroll() {
		if (isScroll)
			return;

		if (time == null)
			time = new Timer();
		time.schedule(new TimerTask() {

			@Override
			public void run() {
				isScroll = true;
				Runnable run = new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub

						if (<span style="color:#ff6666;">isTouch</span>) {//isTouch是在touch监听接口中赋值,来监听是否是用户正在触摸屏幕,如果是将会不做任何处理,也就实现了,解决了当用户想要手动查看轮播的内容时,会自动换掉当前图片

						} else {
							int current = viewpager.getCurrentItem();

							if (current < adapter.getCount() - 1) {
								viewpager.setCurrentItem(current + 1);
							} else {
								viewpager.setCurrentItem(0);
							}
						}
					}

				};

				viewpager.post(run);//UI的操作都要在主线程中,所以 用viewpager  post方法来更改UI,否则会报错
			}

		}, 500, 2000);

	}
public void stopScroll() {
		if (time != null) {
			time.cancel();
			time = null;
		}
	}

其中 startScroll()方法是在Activity或者 Fragment中初始化之后,调用的

 
 
<span style="font-family: Arial, Helvetica, sans-serif;">3、底部指示器的绘制</span>
<pre name="code" class="java" style="font-family: Arial, Helvetica, sans-serif;"> 
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
/**
 * viewpager 底部下标指示器
 * @author ML  2015-05-29
 *
 */
public class AutoScrollViewPagerStateChange extends View {
	public AutoScrollViewPagerStateChange(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	private int size, current;

	@SuppressLint("NewApi")
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		Paint p = new Paint();
		p.setAntiAlias(true);
		p.setAlpha(1);
 		int item_width = 30;
 		int item_height=5;
 		int jiange=20;
		int center_x = (getWidth() - item_width * size-jiange*(size)) / 2;
        int lastright = 0;
 		for (int i = 0; i < size; i++) {
			p.setColor(Color.parseColor("#ff00ff"));//表示不是当前页面的颜色
			p.setStyle(Style.FILL);
			if (i == current) {
				p.setStyle(Style.FILL);
				p.setColor(Color.WHITE);<span style="font-family: Arial, Helvetica, sans-serif;">//表示 当前页面的颜色</span>

			}
<span style="font-family: Arial, Helvetica, sans-serif;">			/*canvas.drawCircle(center_x + item_width / 2 + i * item_width,</span><span style="font-family: Arial, Helvetica, sans-serif;">getHeight() / 2, 10, p);*/
</span><span style="font-family: Arial, Helvetica, sans-serif;">	int left=center_x + item_width / 2 + i * item_width</span><span style="font-family: Arial, Helvetica, sans-serif;">	
	if(i!=0)left=lastright+jiange;
</span><pre code_snippet_id="1780518" snippet_file_name="blog_20160723_24_8667830" name="code" class="java" style="font-family: Arial, Helvetica, sans-serif;">	int right=left+item_width;
              lastright=right;

  			int top=0;
			int bottom=item_height;
 			Rect r=new Rect(left,top,right,bottom);
			canvas.drawRect(r, p);
		
			
			
 		}
		canvas.save();
 	}

	public void drawCicle(int size, int current) {
		this.size = size;
		this.current = current;
		invalidate();
	}
}
 
 
<span style="font-family:Arial, Helvetica, sans-serif;">指示器是通过  canvas paint 来绘制的 通过计算 每一个  长方形的  起始位置  来动态的  改变指示器,在当前下标的 长方形哪里改变一下  paint的颜色值,就可以实现底部切换的效果</span>
如有错误,欢迎指正
源代码:https://github.com/MengLeiGitHub/AutoScrollViewPager
 
 







  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值