Android開發之自訂View專題(四):自訂ViewGroup

來源:互聯網
上載者:User

標籤:移動   viewpager   slidemenu   自訂viewgroup   觸摸事件   

     有時候,我們會有這樣的需求,一個activity裡面需要有兩個或者多個介面切換,就像Viewpager那樣。但是在這些介面裡面又需要能夠有listView,gridview等組件。如果是縱向的,似乎還好,沒什麼影響,那麼如果是橫向的,那麼就會出事情。因為Viewpager會攔截觸摸事件。而如果將Viewpager的觸摸事件攔截掉給裡面的子控制項,那麼Viewpager又不能響應滑動事件了。那麼如何又能讓介面之間能夠來回切換,又能讓裡面的子控制項的觸摸事件也能毫無影響的響應呢,這個時候,我們需要自訂一個Viewgroup,重寫裡面的觸摸攔截方法即可。

     博主自訂的ViewGroup類似於SlideMenu,包含兩個介面的來回切換,博主特意放了一個可以橫向滑動item的listView組件在的個介面實驗,這個listView控制項也是博主之前從網上找出來用的,還不錯的一個控制項。詳細看:




好了,老規矩,完整項目:

http://download.csdn.net/detail/victorfreedom/8329667

有興趣的同學可以下載下來研究學習。

自訂ViewGroup詳細代碼:




package com.freedom.slideviewgroup.ui;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.view.animation.AccelerateInterpolator;import android.widget.Scroller;import com.freedom.slideviewgroup.FreedomApplication;import com.freedom.slideviewgroup.utils.DptoPxUtil;/** * @ClassName: SlideMenu * @author victor_freedom ([email protected]) * @createddate 2015-1-5 下午8:00:36 * @Description: TODO */public class SlideMenu extends ViewGroup {private Context mContext;// 預設第一個private int currentScreen = 0; // 當前屏// 移動控制者private Scroller mScroller = null;// 判斷是否可以移動private boolean canScroll = false;// 是否攔截點擊事件,false表示攔截,true表示將點擊事件傳遞給子控制項private boolean toChild = false;// 處理觸摸事件的移動速率標準public static int SNAP_VELOCITY = 600;// 觸發move的最小滑動距離private int mTouchSlop = 0;// 最後一點的X座標private float mLastionMotionX = 0;// 處理觸摸的速率private VelocityTracker mVelocityTracker = null;// 左右子控制項的監聽器private LeftListener leftListener;private RightListener rightListener;// 觸摸狀態private static final int TOUCH_STATE_REST = 0;private static final int TOUCH_STATE_SCROLLING = 1;private int mTouchState = TOUCH_STATE_REST;// 響應觸摸事件的邊距判定距離(這個根據自訂響應)public static int TOHCH_LEFT = 140;public static int TOHCH_RIGHT = FreedomApplication.mScreenWidth;public static final String TAG = "SlideMenu";public SlideMenu(Context context) {super(context);mContext = context;init();}public SlideMenu(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;init();}/** * @Title: init * @Description: 初始化滑動相關的東西 * @throws */private void init() {mScroller = new Scroller(mContext, new AccelerateInterpolator());mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();}/** * @Title: onMeasure * @Description: 設定viewGroup大小 * @param widthMeasureSpec * @param heightMeasureSpec * @throws */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);setMeasuredDimension(width, height);for (int i = 0; i < getChildCount(); i++) {getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);}}/** * @Title: onLayout * @Description: 設定子控制項的分布位置 * @param changed * @param l *            left * @param t *            top * @param r *            right * @param b *            bottom * @throws */@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int startLeft = 0; // 每個子視圖的起始布局座標int childCount = getChildCount();for (int i = 0; i < childCount; i++) {View child = getChildAt(i);child.layout(startLeft, 0, startLeft + getWidth(), getHeight());startLeft = startLeft + getWidth(); // 校準每個子View的起始布局位置}}/** * @Title: onInterceptTouchEvent * @Description:觸摸事件攔截判定 * @param ev * @return * @throws */@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {final int action = ev.getAction();// 如果當前正在滑動狀態,則攔截事件if ((action == MotionEvent.ACTION_MOVE)&& (mTouchState != TOUCH_STATE_REST)) {return true;}final float x = ev.getX();switch (action) {case MotionEvent.ACTION_DOWN:mLastionMotionX = x;// 判斷目前狀態mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST: TOUCH_STATE_SCROLLING;// 判斷是否能夠響應滑動事件if ((ev.getX() < DptoPxUtil.dip2px(mContext, TOHCH_LEFT) && currentScreen == 1)|| (ev.getX() > TOHCH_RIGHT- DptoPxUtil.dip2px(mContext, 200) && currentScreen == 0)) {canScroll = true;toChild = false;return super.onInterceptTouchEvent(ev);} else {// 如果不能則不攔截事件canScroll = false;toChild = true;return false;}case MotionEvent.ACTION_MOVE:if (toChild) {return false;}final int differentX = (int) Math.abs(mLastionMotionX - x);// 超過了最小滑動距離,並且沒有傳遞事件給子控制項,則更改狀態if (differentX > mTouchSlop) {mTouchState = TOUCH_STATE_SCROLLING;}break;case MotionEvent.ACTION_UP:if (toChild) {return false;}mTouchState = TOUCH_STATE_REST;break;}return super.onInterceptTouchEvent(ev);}/** * @Title: onTouchEvent * @Description: 觸摸事件響應 * @param event * @return * @throws */public boolean onTouchEvent(MotionEvent event) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}// 擷取移動的速率mVelocityTracker.addMovement(event);super.onTouchEvent(event);// 手指位置地點float x = event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:// 如果螢幕的動畫還沒結束,你就按下了,我們就結束該動畫if (mScroller != null) {if (!mScroller.isFinished()) {mScroller.abortAnimation();}}mLastionMotionX = x;break;case MotionEvent.ACTION_MOVE:if (canScroll) {int detaX = (int) (mLastionMotionX - x);mLastionMotionX = x;// 移動距離scrollBy(detaX, 0);}break;case MotionEvent.ACTION_UP:final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000);int velocityX = (int) velocityTracker.getXVelocity();// 滑動速率達到了一個標準(快速向右滑屏,返回上一個螢幕) 馬上進行切屏處理if (velocityX > SNAP_VELOCITY && currentScreen > 0 && canScroll) {changedScreen(currentScreen - 1);}// 快速向左滑屏,返回下一個螢幕)else if (velocityX < -SNAP_VELOCITY&& currentScreen < (getChildCount() - 1) && canScroll) {changedScreen(currentScreen + 1);}// 以上為快速移動的 ,強制切換畫面else {// 如果移動緩慢,那麼先判斷是保留在本螢幕還是到下一螢幕snapToDestination();}if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}mTouchState = TOUCH_STATE_REST;break;case MotionEvent.ACTION_CANCEL:mTouchState = TOUCH_STATE_REST;break;}return super.onInterceptTouchEvent(event);}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {// 如果返回true,則代表正在類比資料,false表示已經停止類比資料scrollTo(mScroller.getCurrX(), mScroller.getCurrY());// 更新位移量postInvalidate();}}/** * @Title: startMove * @Description: 這是從第一個螢幕跳轉到第二個螢幕的快捷方法 * @throws */public void startMove() {if (currentScreen == 1) {return;}if (currentScreen == 0 && rightListener != null) {new Thread(new Runnable() {@Overridepublic void run() {rightListener.postNotifyDataChange();}}).start();}currentScreen++;mScroller.startScroll((currentScreen - 1) * getWidth(), 0, getWidth(),0, 600);// 重新整理介面invalidate();// invalidate -> drawChild -> child.draw -> computeScroll}/** * @Title: startMoves * @Description: 跳轉到第一個螢幕 * @throws */public void startMoves() {changedScreen(0);}/** * @Title: snapToDestination * @Description: 當緩慢移動的時候,判斷跳轉螢幕 * @throws */private void snapToDestination() {int destScreen = (getScrollX() + getWidth() / 3) / getWidth();changedScreen(destScreen);}/** * @Title: changedScreen * @Description: 跳轉螢幕 * @param whichScreen * @throws */private void changedScreen(int whichScreen) {currentScreen = whichScreen;if (currentScreen > getChildCount() - 1) {currentScreen = getChildCount() - 1;}if (currentScreen == 0 && leftListener != null) {leftListener.notifyDataChange();}if (currentScreen == 1 && rightListener != null) {rightListener.notifyDataChange();}// getScrollX得到的是當前視圖相對於父控制項的位移量。初始值是0,int dx = currentScreen * getWidth() - getScrollX();// dx為正值時,螢幕向右滑動,dx為負值時,螢幕向左滑動mScroller.startScroll(getScrollX(), 0, dx, 0, 600);postInvalidate();}public interface LeftListener {public void notifyDataChange();}public interface RightListener {public void notifyDataChange();public void postNotifyDataChange();}public void setLeftListener(LeftListener leftListener) {this.leftListener = leftListener;}public void setRightListener(RightListener rightListener) {this.rightListener = rightListener;}}


自此自訂View專題已經講解完畢,相信所有人都對自訂View有了一個初步的認識,基本上就是那麼幾個步驟。而自訂ViewGroup相對於自訂View多了一個步驟在於要重寫onLayout方法來擺放包含在裡面的子控制項,其餘的,都差不多。

     

好了,老規矩,完整項目:

http://download.csdn.net/detail/victorfreedom/8329667

有興趣的同學可以下載下來研究學習。

自訂ViewGroup詳細代碼:

Android開發之自訂View專題(四):自訂ViewGroup

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.