在實現該控件之前,先說一下該控件的難度,
一、
每個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