Android事件分發學習應用-圖片輪播實現

來源:互聯網
上載者:User

Android事件分發學習應用-圖片輪播實現

前一篇寫到Android事件分發機制學習筆記,下面我們通過一個執行個體的應用來實踐理解下Android事件分發的機制。我們這裡來實現一個圖片的輪播功能,最後順便實現片的自動輪播。

我們的圖片輪播是封裝在一個ViewGroup裡,當我們進行橫向滑動的時候,我們需要阻止事件從ViewGroup往子控制項分發,ViewGroup來消費我們當前的滑動圖片何去何從。下面我們貼出我們的封裝的ViewGroup的代碼實現如下:

 

public class ImageSwitcher extends ViewGroup {private String TAG = ImageSwitcher.class.getSimpleName();private static final int SNAP_VELOCITY = 300;private Scroller scroller;private VelocityTracker mVelocityTracker;private int mTouchSlop;private float mMotionX;private int mImageWidth; private int imageCount;private int mIndex;private int mImageHeight;private int[] imageItems;private boolean forceToRelayout;private int mTouchState = TOUCH_STATE_REST;private static final int TOUCH_STATE_REST = 0;private static final int TOUCH_STATE_SCROLLING = 1;private static final int AUTO_MSG = 0;private static final int START_MSG =2;private static final int HANDLE_MSG = 1;private static final long PHOTO_CHANGE_TIME = 4000;private  Handler mHandler = new Handler(){ //處理圖片自動或者手動滾動操作public void handleMessage(Message msg) {switch (msg.what) {case AUTO_MSG:scrollToNext();mHandler.sendEmptyMessageDelayed(AUTO_MSG, PHOTO_CHANGE_TIME);break;case START_MSG:mHandler.sendEmptyMessageDelayed(AUTO_MSG, PHOTO_CHANGE_TIME);break;case HANDLE_MSG:mHandler.removeMessages(AUTO_MSG);mHandler.sendEmptyMessageDelayed(AUTO_MSG, PHOTO_CHANGE_TIME);default:break;}}};/** * 表示滾動到下一張圖片這個動作 */private static final int SCROLL_NEXT = 0;/** * 表示滾動到上一張圖片這個動作 */private static final int SCROLL_PREVIOUS = 1;private static final int SCROLL_BACK = 2;public ImageSwitcher(Context context, AttributeSet attrs) {super(context, attrs);scroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();}/** * 當View被添加到Window容器的時候才開始執行:生命週期依次先後 onMeasure > onLayout > onDraw >onAttachedToWindow  */@Overrideprotected void onAttachedToWindow(){super.onAttachedToWindow();mHandler.sendEmptyMessage(START_MSG); //發送訊息讓圖片自動開始滾動}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if(changed || forceToRelayout){imageCount = getChildCount();mImageWidth = getMeasuredWidth();mImageHeight = getMeasuredHeight();int marginLeft = 0;scroller.abortAnimation(); //設定scroller為滾動狀態this.scrollTo(0, 0); //每次重新布局時候,重設滾動初始位置int[] items = { getIndexForItem(1), getIndexForItem(2),getIndexForItem(3), getIndexForItem(4),getIndexForItem(5) };imageItems = items;for (int i = 0; i < items.length; i++) {ImageView childView = (ImageView)getChildAt(items[i]);childView.layout(marginLeft, 0, marginLeft+ mImageWidth , mImageHeight);marginLeft = marginLeft + mImageWidth;}refreshImageView();forceToRelayout = false;}}private void refreshImageView(){for (int i = 0; i < imageItems.length; i++) {ImageView childView = (ImageView)getChildAt(imageItems[i]);childView.invalidate();}}private int getIndexForItem(int item) {int index = -1;index = mIndex + item - 3;while (index < 0) {index = index + imageCount;}while (index > imageCount - 1) {index = index - imageCount;}return index;}    @Overridepublic boolean  onInterceptTouchEvent(MotionEvent ev) {int action = ev.getAction();if ((action == MotionEvent.ACTION_MOVE)&& (mTouchState != TOUCH_STATE_REST)) {return true;}float xLoc = ev.getX();switch(action){case MotionEvent.ACTION_DOWN:mMotionX = xLoc;mTouchState = TOUCH_STATE_REST;Log.e(TAG, onInterceptTouchEvent ACTION_DOWN);  break;case MotionEvent.ACTION_MOVE:Log.e(TAG, onInterceptTouchEvent ACTION_MOVE);   int xDif = (int)Math.abs(mMotionX - xLoc); if(xDif > mTouchSlop){  //當我們的水平距離滾動達到我們滾動的最小距離,開始攔截ViewGroup的事件給子控制項分發 mTouchState = TOUCH_STATE_SCROLLING; }break;case MotionEvent.ACTION_UP:Log.e(TAG, onInterceptTouchEvent ACTION_UP);  mTouchState = TOUCH_STATE_REST;break;case MotionEvent.ACTION_CANCEL:Log.e(TAG, onInterceptTouchEvent ACTION_CANCEL);mTouchState = TOUCH_STATE_REST;break;default:Log.e(TAG, onInterceptTouchEvent DEFAULT);mTouchState = TOUCH_STATE_REST;    break;}return mTouchState != TOUCH_STATE_REST;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if(scroller.isFinished()){ //scroller還沒有開始或者已經完成,以下代碼在手指滑動的時候才開始執行if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);int action = event.getAction();float x = event.getX();switch (action) {case MotionEvent.ACTION_DOWN:// 記錄按下時的橫座標mMotionX = x;case MotionEvent.ACTION_MOVE:int disX = (int)(mMotionX - x);mMotionX = x;scrollBy(disX, 0);break;case MotionEvent.ACTION_UP:mVelocityTracker.computeCurrentVelocity(1000);int velocityX = (int) mVelocityTracker.getXVelocity();if (judeScrollToNext(velocityX)) {// 下一張圖scrollToNext();} else if (judeScrollToPrevious(velocityX)) {//上一張圖scrollToPrevious();} else {// 當前圖片scrollBack();}if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}mHandler.sendEmptyMessageDelayed(HANDLE_MSG, PHOTO_CHANGE_TIME);return true;}}return false; }private void scrollBack() {if (scroller.isFinished()) {beginScroll(getScrollX(), 0, -getScrollX(), 0,SCROLL_BACK);}}private void scrollToPrevious() {if(scroller.isFinished()){setImageSwitchIndex(SCROLL_PREVIOUS);int disX = -mImageWidth - getScrollX();beginScroll(getScrollX(), 0, disX, 0,SCROLL_PREVIOUS);}}private void scrollToNext() {if (scroller.isFinished()) {setImageSwitchIndex(SCROLL_NEXT);int disX = mImageWidth - getScrollX();beginScroll(getScrollX(), 0, disX, 0,SCROLL_NEXT);}}/** * 圖片開始滑動 */private void beginScroll(int startX, int startY, int dx, int dy,final int action) {int duration = (int) (700f / mImageWidth * Math.abs(dx));scroller.startScroll(startX, startY, dx, dy, duration);invalidate();mHandler.postDelayed(new Runnable() {@Overridepublic void run() {if (action == SCROLL_NEXT || action == SCROLL_PREVIOUS) {forceToRelayout = true;requestLayout();}}}, duration);}private void setImageSwitchIndex(int action) {if(action == SCROLL_NEXT){if(mIndex < imageCount){mIndex++;}else{mIndex = 0;}}else if(action == SCROLL_PREVIOUS){if(mIndex > 0){mIndex--;}else{mIndex = imageCount -1;}}}/** * 判斷時候滑向前一個 * @param velocityX * @return */private boolean judeScrollToPrevious(int velocityX) {return velocityX > SNAP_VELOCITY || getScrollX() < -mImageWidth / 2;}/** * 判斷時候滑向後一個 * @param velocityX * @return */private boolean judeScrollToNext(int velocityX) {return velocityX < -SNAP_VELOCITY|| getScrollX() > mImageWidth / 2;}@Overridepublic void computeScroll() {if (scroller.computeScrollOffset()) {scrollTo(scroller.getCurrX(), scroller.getCurrY());    //重新整理View 否則效果可能有誤差postInvalidate();}}}

 

從程式碼分析我們知道,我們在判斷手指事件的時候,我們在ACTION_MOVE的時候,我們判斷當水平移動距離可判為大於移動的最小距離,我們這個時候攔截VIewGroup往下分發的事件,事件這個時候會走ViewGroup的onTouchEvent來消費事件,我們把讀圖片的輪滑處理在onTouchEvent裡來處理。

這個Demo裡我們要注意的一點,我們來看我們的布局檔案如下:

 

                                              
我們看到我們對ImageView設定了clickable = true的屬性,如果這裡我們去掉該屬性,我們就不能手動去滑動圖片了。

 

大家一定會好奇這是為什嗎?下面我們來根據前一篇的Android事件分發機制學習筆記 的事件分發分析來做出解答。我們通過分析,如果我們不給ImageView設定clickable=true,ImageView的onEventTouch嘗試消費時候會發現 if (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) 進到判斷裡去,onEventTouch 傳回值為false,那ImageView的dispatchTouchEvent返回為false,意思就是ImageView控制項是預設不消費事件的。還記得我們在Android的事件分發中提到,當我們的一次事件從Activity開始分發到葉子控制項,到葉子控制項開始一層一層回溯嘗試消費事件,還記得我們上次說的事件“記憶”功能,當我們從Activity分發的事件一直回溯到Activity都沒有被消費掉,後面的事件就不會從根控制項DecorView繼續往下分發。

下面我們要問,那我們不給ImageView 設定clickable = true,有沒有辦法讓圖片可以滑動呢?答案當然有,辦法一,我們這裡用Button來代替ImageVIew;辦法二,我們ImageView沒有消費掉事件,我們的事件就會回溯到ViewGroup去嘗試消費,我們可以在VIewGroup的onTouchEvent去消費,通過修改onTouchEvent的傳回值為true,來達到消費的效果。那我們後面的動作也就會記住事件分發消費“迴路”,我們的後續事件也就能得到消費,我們方法二的辦法就是,在ViewGroup裡去返回onTouchEvent的傳回值來消費,此處我們完全可以把事件攔截onInterceptTouchEvent注釋掉,同樣達到我們上面代碼的效果。以上是對圖片輪播的事件處理過程的主要講解,代碼裡還加入處理圖片自動輪播的代碼。

後面有時間,我會來嘗試分析更複雜的事件分發的過程,比如,ListView的基類AbsLIstView已經加入了自己的事件分發、攔截處理,我們怎麼對ListView做我們自己的事件分發攔截處理,歡迎大家來拍磚。最後附片輪播例子的Demo.

 

 

 


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.