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









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章