Android自定义ViewGroup

来源:互联网 发布:中国临床试验数据库 编辑:程序博客网 时间:2024/06/05 01:52

Android自定义ViewGroup

以Dome为例,来一步一步的讲解如何自定义ViewGoup,先看下面Demo,此Demo模仿ViewPage的滑动。

这里写图片描述

从demo中可以看出,红、蓝、黄分别装载在ViewGroup中,随着手势的滑动来显示相应的View。那么ViewGroup是什么呢?

这里写图片描述

从上图我们大概可以猜到ViewGroup是什么了,此刻我在想你们是否跟我想的一样,好我们来去看看官方的解释,看是否跟大家猜的一样:

“A ViewGroup is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers. ”

翻译过来大概意思是:ViewGroup是一种特殊的视图可以包含其他视图(称为孩子)的视图组基类的布局和视图的容器。

那ViewGroup怎么实现呢?这个简单,按官方文档上面的例子,实践并总结出除了构造方法之外onMeasure()、onLayout(),这两方法是必须实现的,不然你的View就无法显示在屏幕上面。看以下示例:

 import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.LinearLayout;/**    * Created by win7 on 2017/7/4.    */public class BaseViewGroup extends ViewGroup {private final String TAG = "BaseViewGroup";private AView centerView;private BView rigthView;private CView leftView;private VelocityTracker mVelocityTracker; //速率捕捉器private final int SCREEN_LEFT = 1;  // 左屏private final int SCREEN_CENTER = 2; //中屏private final int SCREEN_RIGHT = 3;//右屏private final int mUnits = 1000;//手势滑动速度private final int mDuration = -1;private int mTouchSlop; //滑动阀值private int mMaximumVelocity; //最大移动速度private int mActivePointerId;//活动触摸点private int mCurrentScreen = 2; //当前所处屏private int mScreenWidth; //屏幕宽度private float mLastMotionX; //上一次移动点的位置private boolean mPointInViews; //触摸点是否在指定范围内private boolean mScrolled; //是否滑动 public BaseViewGroup(Context context) {    this(context, null); } public BaseViewGroup(Context context, AttributeSet attrs) {    this(context, attrs, -1); } public BaseViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init(context); } private void init(Context context) {    //获取屏幕宽度    DisplayMetrics displayMetrics = new DisplayMetrics();    WindowManager windowManager = (WindowManager) context            .getSystemService(Context.WINDOW_SERVICE);    windowManager.getDefaultDisplay().getMetrics(displayMetrics);    mScreenWidth = displayMetrics.widthPixels;    LinearLayout.LayoutParams aLayoutParams = new LinearLayout.LayoutParams(            LinearLayout.LayoutParams.MATCH_PARENT,            LinearLayout.LayoutParams.WRAP_CONTENT);    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(            LinearLayout.LayoutParams.MATCH_PARENT,            LinearLayout.LayoutParams.MATCH_PARENT);    //中屏    centerView = new AView(context);    this.addView(centerView, aLayoutParams);    //右屏    rigthView = new BView(context);    this.addView(rigthView, layoutParams);    //左屏    leftView = new CView(context);    this.addView(leftView, layoutParams);    //获取标准的常量用来设置UI的超时、大小和距离    ViewConfiguration configuration = ViewConfiguration.get(context);    //获取用户认为滚动之前,触摸可以移动像素的距离。    mTouchSlop = configuration.getScaledTouchSlop();    //获取滑动的最大速度, 以像素/每秒来进行计算    mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();}/** *要求所有子View测量自己并计算这一点的测量值 *基于View的布局. */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    //测量原始的大小    int defaultWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);    int defaultHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);    //测量子View的大小    final int count = getChildCount();    for (int i = 0; i < count; i++) {        View child = getChildAt(i);        measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);    }    //保存测量结果    setMeasuredDimension(defaultWidth, defaultHeight);}/** *在布局中放置所有的子节点 */ @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {    int width = this.getWidth();    int height = this.getHeight();    //对子View进行排版    centerView.layout(0, 0, width, height);    rigthView.layout(0, 0, width, height);    leftView.layout(0, 0, width, height);    //改变left和right的坐标点位置,以达到左、中、右三屏效果    rigthView.scrollTo(-width, 0);    leftView.scrollTo(width, 0); }@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {    return super.dispatchTouchEvent(ev);}/** *判断触摸点是否在子View上 *@param ev *@return */public boolean pointInViews(MotionEvent ev) {    boolean rigthView = false;    float x = ev.getX();    float y = ev.getY();    if (mCurrentScreen == SCREEN_CENTER) {        return centerView.pointInViews(x, y);    } else if (mCurrentScreen == SCREEN_LEFT) {        return leftView.pointInViews(x, y);    } else if (mCurrentScreen == SCREEN_RIGHT) {        return this.rigthView.pointInViews(x, y);    }    return rigthView;}/**事件拦截*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {    mPointInViews = pointInViews(ev);    if (!mPointInViews) {        return false;    }    final float x = ev.getX();    final float y = ev.getY();    // 防止事件冲突    getParent().requestDisallowInterceptTouchEvent(true);    boolean handle = super.onInterceptTouchEvent(ev);    switch (ev.getAction()) {        case MotionEvent.ACTION_DOWN:            mLastMotionX = x;            //获取手势触控点            if (ev.getPointerCount() > 0) {                mActivePointerId = ev.getPointerId(0);            }            break;        case MotionEvent.ACTION_MOVE:            //根据手势滑动距离来判断是否拦截此次手势事件            if (Math.abs(x - mLastMotionX) >= mTouchSlop) {                handle = true;            }            break;        case MotionEvent.ACTION_UP:            break;    }    return handle;}@Overridepublic boolean onTouchEvent(MotionEvent event) {    if (!mPointInViews) {        return false;    }    if (null == mVelocityTracker) {        //初始化速率捕捉器        mVelocityTracker = VelocityTracker.obtain();    }    if (null != mVelocityTracker) {        //删除手势        mVelocityTracker.addMovement(event);    }    switch (event.getAction() & MotionEvent.ACTION_MASK) {        case MotionEvent.ACTION_DOWN:            //记录手势按下的X坐标点            mLastMotionX = event.getX();            mScrolled = false;            break;        case MotionEvent.ACTION_MOVE:            //是否有手势触控点            final int activePointerIndex = event.getPointerCount() > mActivePointerId ? event.findPointerIndex(mActivePointerId) : -1;            if (activePointerIndex == -1) {                break;            }            //获取手势触控点的X坐标            int x = (int) event.getX(activePointerIndex);            //计算滑动过程            int move = (int) mLastMotionX - x;            if (mScrolled) {                mLastMotionX = x;                onTouchMove(move);            } else if (Math.abs(x - mLastMotionX) >= mTouchSlop) {                mScrolled = true;                onTouchMove(move);            }            break;        case MotionEvent.ACTION_POINTER_UP:        case MotionEvent.ACTION_UP:            if (mActivePointerId < 0) {                break;            }            final int activeUpPointerIndex = event.getPointerCount() > mActivePointerId ? event.findPointerIndex(mActivePointerId) : -1;            if (activeUpPointerIndex == -1) {                break;            }            if (event.getPointerId(event.getActionIndex()) != mActivePointerId) {                break;            }            //计算当前手势的速度            mVelocityTracker.computeCurrentVelocity(mUnits, mMaximumVelocity);            //获取当前手势X移动的速度            float mVelocityX = mVelocityTracker.getXVelocity();            mActivePointerId = -1;            scrollToNextScreen(mVelocityX);            break;        case MotionEvent.ACTION_POINTER_DOWN:            break;        case MotionEvent.ACTION_CANCEL:            releaseTracker();    }    return true;}/** *释放速率捕捉器 */private void releaseTracker() {    if (mVelocityTracker != null) {        mVelocityTracker.recycle();        mVelocityTracker = null;    }}/** *根据X坐标位移 */private void onTouchMove(int move) {    if (mCurrentScreen == SCREEN_LEFT) {        leftView.scrollBy(move, 0);        centerView.scrollBy(move, 0);    } else if (mCurrentScreen == SCREEN_CENTER) {        centerView.scrollBy(move, 0);        leftView.scrollBy(move, 0);        rigthView.scrollBy(move, 0);    } else if (mCurrentScreen == SCREEN_RIGHT) {        rigthView.scrollBy(move, 0);        centerView.scrollBy(move, 0);    }}/** *根据手势的速度来判断滑动的方向 *@param mVelocityX 当前手势X移动的速度 */private void scrollToNextScreen(float mVelocityX) {    boolean isScrollToNextScreen = false;    if (mVelocityX > mUnits) {        isScrollToNextScreen = false;    } else if (mVelocityX < -mUnits) {        isScrollToNextScreen = true;    } else {        if (leftView.isHalfDisplaying()) {            // left            snapTo(SCREEN_LEFT, mScreenWidth);        } else if (rigthView.isHalfDisplaying()) {            // right            snapTo(SCREEN_RIGHT, mScreenWidth);        } else {            snapTo(SCREEN_CENTER, mScreenWidth);        }        return;    }    int nextScreen;    if (leftView.isScrolling() || leftView.isAllDisplaying()) {        nextScreen = SCREEN_LEFT;    } else if (rigthView.isAllDisplaying()) {        nextScreen = SCREEN_RIGHT;    } else {        nextScreen = SCREEN_CENTER;    }    if (isScrollToNextScreen) {        nextScreen++;    } else {        if (nextScreen != SCREEN_LEFT && mCurrentScreen != SCREEN_RIGHT) {            nextScreen--;        }    }    if (nextScreen > SCREEN_RIGHT) {        snapTo(nextScreen, mScreenWidth);    } else if (nextScreen == SCREEN_RIGHT) {        snapTo(nextScreen, mScreenWidth);    } else if (nextScreen <= SCREEN_RIGHT && nextScreen >= SCREEN_LEFT) {        snapTo(nextScreen, mScreenWidth);    }} /**  *滑动到某一屏  *@param toCurrent  *@param x  */private void snapTo(int toCurrent, int x) {    if (SCREEN_LEFT == toCurrent) {        leftView.snapTo(0, mDuration);        centerView.snapTo(-x, mDuration);        rigthView.snapTo(-x, 0);        mCurrentScreen = 1;    } else if (SCREEN_CENTER == toCurrent) {        centerView.snapTo(0, mDuration);        rigthView.snapTo(-x, mDuration);        leftView.snapTo(x, mDuration);        mCurrentScreen = SCREEN_CENTER;    } else if (SCREEN_RIGHT == toCurrent) {        rigthView.snapTo(0, mDuration);        centerView.snapTo(x, mDuration);        leftView.snapTo(x, mDuration);        mCurrentScreen = SCREEN_RIGHT;    }}

}

onMeasure()、onLayout()这个两个重要的函数,我会在后面的ViewGroup、View之onMeasure、onLayout解刨中详细讲解。
onInterceptTouchEvent 我会在后面的View和ViewGroup事件分发机制中讲解

到此,对Viewgroup的讲解就暂告一段落,建议大家动手实践,附上Dmoe源码

原创粉丝点击