一属性变量分析

构造函数完成获取attr属性内容的读取,读取用户配置的UI属性,用于构造新的UI结构。

属性内容为,注意这里的SlidingShow作者自己定义的,拷自源码包:

<resources>    <declare-styleable name="SlidingShow">        <attr name="handle" format="reference" />        <attr name="content" format="reference" />        <attr name="orientation"  format="integer" />        <attr name="bottomOffset" format="dimension" />        <attr name="topOffset" format="dimension" />        <attr name="allowSingleTap" format="boolean" />        <attr name="animateOnClick" format="boolean" />    </declare-styleable></resources>
android:allowSingleTap:指示是否可以通过handle打开或关闭


android:animateOnClick:指示是否当使用者按下手柄打开/关闭时是否该有一个动画。


android:content:隐藏的内容

android:handle:handle(手柄)

二源码情景分析

在分析源码前,先要选择一个分析顺序,顺序按照ViewGroup绘制周期来进展。


根据viewGroup的绘制顺序开始分析源码。

2.1 测量函数

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {(1)        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        final View handle = mHandle;        measureChild(handle, widthMeasureSpec, heightMeasureSpec);//(2)获取child View的大小        //定义mContent大小        if (mVertical) {            int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;(3)            mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY),                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));        } else {            int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;            mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),                    MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY));        }        setMeasuredDimension(widthSpecSize, heightSpecSize);    }
(1) widthMeasureSpec和heightMeasureSpec为宽度规格和高度规格,可以获取对应的mode(AT_MOST尽大/ EXACTLY精确/ UNSPECIFIED不限制),可以获取对应尺寸。

(2) 获取viewGroup中子视图的尺寸,这里是获取handle的View大小。

(3) 根据对应的偏移量和handle view的大小,设置Content大小。

2.2 视图函数

    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {(1)        if (mTracking) {            return;        }        final int width = r - l;    //viewGroup的宽度        final int height = b - t;   //viewGroup的高度        final View handle = mHandle;        int childWidth = handle.getMeasuredWidth();     //handle尺寸        int childHeight = handle.getMeasuredHeight();        int childLeft;        int childTop;        final View content = mContent;        if (mVertical) {            childLeft = (width - childWidth) / 2;            /**             * 设定hanlde位置,这里handle与mBottomOffset和自己大小有关             * 设定content位置,这里content与mTopOffset和handle大小有关             */            childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset;(2)            //设定content的位置            content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(),                    mTopOffset + childHeight + content.getMeasuredHeight());        } else {            childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset;            childTop = (height - childHeight) / 2;            content.layout(mTopOffset + childWidth, 0,                    mTopOffset + childWidth + content.getMeasuredWidth(),                    content.getMeasuredHeight());        }        //确定handle位置        handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);(3)        mHandleHeight = handle.getHeight();        mHandleWidth = handle.getWidth();    }
(1)参数changed表示view有新的尺寸或位置;参数l表示相对于父view的Left位置;参数t表示相对于父view的Top位置;参数r表示相对于父view的Right位置;参数b表示相对于父view的Bottom位置。

(2) 问号表达式,根据条件展开或是收缩决定handle的Top位置。

(3) 确定handle位置,包含了展开和收缩的判定,这里Content位置还不太对,还显示在expanded(展开状态)。

2.3 绘制图像函数

    @Override    protected void dispatchDraw(Canvas canvas) {    //绘制两个子view        final long drawingTime = getDrawingTime();  //获取当前GPU绘制view时间,不是日期时间(1)        final View handle = mHandle;        final boolean isVertical = mVertical;        drawChild(canvas, handle, drawingTime);(1)        if (mTracking || mAnimating) {      //判断当前状态是否为追踪或者发生动画状态            final Bitmap cache = mContent.getDrawingCache();            if (cache != null) {                if (isVertical) {                    canvas.drawBitmap(cache, 0, handle.getBottom(), null);                } else {                    canvas.drawBitmap(cache, handle.getRight(), 0, null);                }            } else {                canvas.save();                canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset,                        isVertical ? handle.getTop() - mTopOffset : 0);(2)                drawChild(canvas, mContent, drawingTime);                canvas.restore();   //更改save方法前所有的绘制修改            }        } else if (mExpanded) {            drawChild(canvas, mContent, drawingTime);(3)        }    }

(1)根据当前配置的canvas绘制handle

(2)更改相应的canvas配置,用户绘制content

(3)根据当前配置的canvas绘制content

2.4 完成绘制函数

    @Override    protected void onFinishInflate() {        mHandle = findViewById(mHandleId);        if (mHandle == null) {            throw new IllegalArgumentException("The handle attribute is must refer to an"                    + " existing child.");        }        mHandle.setOnClickListener(new DrawerToggler());//设置单击监听类(1)        mContent = findViewById(mContentId);        if (mContent == null) {            throw new IllegalArgumentException("The content attribute is must refer to an"                    + " existing child.");        }        mContent.setVisibility(View.GONE);    }

(1)设置监听类,内部设置相应的点击事件动画。

2.4.1 监听类的解析

    private class DrawerToggler implements OnClickListener {        public void onClick(View v) {            if (mLocked) {(1)                return;            }            // mAllowSingleTap isn't relevant here; you're *always*            // allowed to open/close the drawer by clicking with the            // trackball.            //android:allowSingleTap:指示是否可以通过handle打开或关闭            //android:animateOnClick:指示是否当使用者按下手柄打开/关闭时是否该有一个动画。            if (mAnimateOnClick) {(2)                animateToggle();(3)            } else {                toggle();(4)            }        }    }
(1)判断是否将view锁住,不允许点击。 (2)判断是否使用单击动画,可以不使用,可以使用。
(3)animateToggle()方法,单击后的动画效果
    public void animateToggle() {        if (!mExpanded) {            animateOpen();        } else {            animateClose();        }    }
展开和收缩两种动画效果:
    public void animateOpen() {        prepareContent();   //准备content(-1)        final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;        if (scrollListener != null) {            scrollListener.onScrollStarted();//调用onScrollStarted函数        }        animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());//展开动画(-2)        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);//设定当前的accessibilityEvent                if (scrollListener != null) {            scrollListener.onScrollEnded(); //调用onScrollEnded函数        }    }
(-1)准备content
    private void prepareContent() {        if (mAnimating) {            return;        }        // Something changed in the content, we need to honor the layout request        // before creating the cached bitmap        final View content = mContent;        if (content.isLayoutRequested()) {            if (mVertical) {                final int childHeight = mHandleHeight;                int height = getBottom() - getTop() - childHeight - mTopOffset;                content.measure(MeasureSpec.makeMeasureSpec(getRight() - getLeft(), MeasureSpec.EXACTLY),                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));                content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(),                        mTopOffset + childHeight + content.getMeasuredHeight());            } else {                final int childWidth = mHandle.getWidth();                int width = getRight() - getLeft() - childWidth - mTopOffset;                content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),                        MeasureSpec.makeMeasureSpec(getBottom() - getTop(), MeasureSpec.EXACTLY));                content.layout(childWidth + mTopOffset, 0,                        mTopOffset + childWidth + content.getMeasuredWidth(),                        content.getMeasuredHeight());            }        }        // Try only once... we should really loop but it's not a big deal        // if the draw was cancelled, it will only be temporary anyway        content.getViewTreeObserver().dispatchOnPreDraw();        if (!content.isHardwareAccelerated()) content.buildDrawingCache();        content.setVisibility(View.GONE);//        mContent.setVisibility(View.VISIBLE);    }
重新绘制content,设计相应的measure() layout()等方法。 跟onLayout(boolean changed, int l, int t, int r, int b)方法中绘制content相同。

(-2)动画展开aimateOpen(boolean)方法
    private void animateOpen(int position) {        prepareTracking(position);//准备路径        performFling(position, -mMaximumAcceleration, true);//执行跳动    }

    private void prepareTracking(int position) {        mTracking = true;//设置标志位        mVelocityTracker = VelocityTracker.obtain();(--1)        boolean opening = !mExpanded;        if (opening) {            mAnimatedAcceleration = mMaximumAcceleration;   //加速度设定            mAnimatedVelocity = mMaximumMajorVelocity;      //最大速度设定            mAnimationPosition = mBottomOffset +                    (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth);            moveHandle((int) mAnimationPosition);   //移动动画(--2)            mAnimating = true;            mHandler.removeMessages(MSG_ANIMATE);(--3)            long now = SystemClock.uptimeMillis();            mAnimationLastTime = now;   //记录时间            mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;            mAnimating = true;        } else {            if (mAnimating) {                mAnimating = false;                mHandler.removeMessages(MSG_ANIMATE);            }            moveHandle(position);(--4)        }    }
(--1) 速度追踪器获取 (--2) 处理移动操作,moveHandle
    private void moveHandle(int position) {        final View handle = mHandle;        if (mVertical) {            if (position == EXPANDED_FULL_OPEN) {//完全展开                handle.offsetTopAndBottom(mTopOffset - handle.getTop());//设定水平偏移量                invalidate();            } else if (position == COLLAPSED_FULL_CLOSED) {//完全关闭                handle.offsetTopAndBottom(mBottomOffset + getBottom() - getTop() -                        mHandleHeight - handle.getTop());                invalidate();            } else {                final int top = handle.getTop();//中间状态                int deltaY = position - top;                if (position < mTopOffset) {                    deltaY = mTopOffset - top;                } else if (deltaY > mBottomOffset + getBottom() - getTop() - mHandleHeight - top) {                    deltaY = mBottomOffset + getBottom() - getTop() - mHandleHeight - top;                }                handle.offsetTopAndBottom(deltaY);                final Rect frame = mFrame;                final Rect region = mInvalidate;                handle.getHitRect(frame);                region.set(frame);                region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY);                region.union(0, frame.bottom - deltaY, getWidth(),                        frame.bottom - deltaY + mContent.getHeight());                invalidate(region);            }        } else {            ......        }    }
(--3) 移除动画消息
(--4) 移动到相应位置
2.4.1总结animateClose();和animateOpen();基本上一样这里就不在描述了。toggle();更是简单了很多,没有对应的动画,这里也不再分析。

2.5 滑动事件处理

前面的介绍中,首先描绘如何绘制一个View,并给出了绘制顺序;后来设计了相应的点击事件处理,并提供了有动画和无动画两种情况下的处理函数;那么最后则是处理滑动或者多点触控的事件。 这里会用到两个方法,都是viewGroup的方法,分别是onInterceptTouchEvent()和onTouchEvent()方法,执行顺序遵循下面五点:
1. down事件首先会传递到onInterceptTouchEvent()方法
2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        if (mLocked) {            return false;        }        final int action = event.getAction();        float x = event.getX();        float y = event.getY();        final Rect frame = mFrame;        final View handle = mHandle;        handle.getHitRect(frame);//找到控件占据的矩形区域的矩形坐标        if (!mTracking && !frame.contains((int) x, (int) y)) {            return false;        }        if (action == MotionEvent.ACTION_DOWN) {            mTracking = true;//规划路径中            handle.setPressed(true);            // Must be called before prepareTracking()            prepareContent();            // Must be called after prepareContent()            if (mOnDrawerScrollListener != null) {                mOnDrawerScrollListener.onScrollStarted();            }            if (mVertical) {                final int top = mHandle.getTop();                mTouchDelta = (int) y - top;                prepareTracking(top);//设定当前位置            } else {                final int left = mHandle.getLeft();                mTouchDelta = (int) x - left;                prepareTracking(left);            }            mVelocityTracker.addMovement(event);        }        return true;//返回true,Event交由onTouchEvent处理    }

    @Override    public boolean onTouchEvent(MotionEvent event) {        if (mLocked) {            return true;        }        if (mTracking) {            mVelocityTracker.addMovement(event);            final int action = event.getAction();            switch (action) {                case MotionEvent.ACTION_MOVE://移动操作                    moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta);                    break;                case MotionEvent.ACTION_UP:                case MotionEvent.ACTION_CANCEL: {                    final VelocityTracker velocityTracker = mVelocityTracker;                    velocityTracker.computeCurrentVelocity(mVelocityUnits);                    float yVelocity = velocityTracker.getYVelocity();                    float xVelocity = velocityTracker.getXVelocity();//计算路径的点                    boolean negative;                    final boolean vertical = mVertical;                    if (vertical) {                        negative = yVelocity < 0;                        if (xVelocity < 0) {                            xVelocity = -xVelocity;                        }                        if (xVelocity > mMaximumMinorVelocity) {                            xVelocity = mMaximumMinorVelocity;                        }                    } else {                        negative = xVelocity < 0;                        if (yVelocity < 0) {                            yVelocity = -yVelocity;                        }                        if (yVelocity > mMaximumMinorVelocity) {                            yVelocity = mMaximumMinorVelocity;                        }                    }                    float velocity = (float) Math.hypot(xVelocity, yVelocity);// sqrt(x2+ y2).                    if (negative) {                        velocity = -velocity;                    }                    final int top = mHandle.getTop();                    final int left = mHandle.getLeft();                    if (Math.abs(velocity) < mMaximumTapVelocity) {                        if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) ||                                (!mExpanded && top > mBottomOffset + getBottom() - getTop() -                                        mHandleHeight - mTapThreshold) :                                (mExpanded && left < mTapThreshold + mTopOffset) ||                                        (!mExpanded && left > mBottomOffset + getRight() - getLeft() -                                                mHandleWidth - mTapThreshold)) {                            if (mAllowSingleTap) {//是否通过点击打开                                playSoundEffect(SoundEffectConstants.CLICK);                                if (mExpanded) {                                    animateClose(vertical ? top : left);                                } else {                                    animateOpen(vertical ? top : left);                                }                            } else {                                performFling(vertical ? top : left, velocity, false);//执行松开手的后面运动(1)                            }                        } else {                            performFling(vertical ? top : left, velocity, false);                        }                    } else {                        performFling(vertical ? top : left, velocity, false);                    }                }                break;            }        }        return mTracking || mAnimating || super.onTouchEvent(event);    }
(1)松开手后的处理函数
private void performFling(int position, float velocity, boolean always) {        mAnimationPosition = position;        mAnimatedVelocity = velocity;   //手势控制速度        if (mExpanded) {            if (always || (velocity > mMaximumMajorVelocity ||                    (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) &&                            velocity > -mMaximumMajorVelocity))) {                // We are expanded, but they didn't move sufficiently to cause                // us to retract.  Animate back to the expanded position.                mAnimatedAcceleration = mMaximumAcceleration;                if (velocity < 0) {                    mAnimatedVelocity = 0;                }            } else {                // We are expanded and are now going to animate away.                mAnimatedAcceleration = -mMaximumAcceleration;                if (velocity > 0) {                    mAnimatedVelocity = 0;                }            }        } else {            if (!always && (velocity > mMaximumMajorVelocity ||                    (position > (mVertical ? getHeight() : getWidth()) / 2 &&                            velocity > -mMaximumMajorVelocity))) {                // We are collapsed, and they moved enough to allow us to expand.                mAnimatedAcceleration = mMaximumAcceleration;                if (velocity < 0) {                    mAnimatedVelocity = 0;                }            } else {                // We are collapsed, but they didn't move sufficiently to cause                // us to retract.  Animate back to the collapsed position.                mAnimatedAcceleration = -mMaximumAcceleration;                if (velocity > 0) {                    mAnimatedVelocity = 0;                }            }        }        long now = SystemClock.uptimeMillis();        mAnimationLastTime = now;        mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;        mAnimating = true;        mHandler.removeMessages(MSG_ANIMATE);        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);        stopTracking();//结束动画    }
计算接下来运动所需要的参量,发送handler执行后面的动画。
   private void doAnimation() {        if (mAnimating) {            incrementAnimation();            if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1) {                mAnimating = false;                closeDrawer();            } else if (mAnimationPosition < mTopOffset) {                mAnimating = false;                openDrawer();            } else {                moveHandle((int) mAnimationPosition);                mCurrentAnimationTime += ANIMATION_FRAME_DURATION;                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),                        mCurrentAnimationTime);//循环消息            }        }    }    private void incrementAnimation() {        long now = SystemClock.uptimeMillis();        float t = (now - mAnimationLastTime) / 1000.0f;                   // ms -> s        final float position = mAnimationPosition;        final float v = mAnimatedVelocity;                                // px/s        final float a = mAnimatedAcceleration;                            // px/s/s        mAnimationPosition = position + (v * t) + (0.5f * a * t * t);     // px        mAnimatedVelocity = v + (a * t);                                  // px/s        mAnimationLastTime = now;                                         // ms    }

    private class SlidingHandler extends Handler {        public void handleMessage(Message m) {            switch (m.what) {                case MSG_ANIMATE:                    doAnimation();                    break;            }        }    }


更多相关文章

  1. Android的事件分发机制
  2. Android(安卓)Animation学习笔记
  3. Android(安卓)模块通信—— EventBus
  4. Android(安卓)开机logo动画
  5. Android(安卓)设置软键盘搜索键以及监听搜索键点击事件
  6. Android(安卓)触控事件解析(Mastering the Android(安卓)Touch Sy
  7. Android开机LOG
  8. Android(安卓)Animation模式详解
  9. android绘图

随机推荐

  1. Android(安卓)项目如何获取MD5和SHA1
  2. Android中广播的基本原理
  3. android jni入门基础
  4. 一个简单的手电筒APP源码分享(支持Android
  5. Android(安卓)Bitmap详解
  6. Android(安卓)View的onClick回调机制
  7. Android(安卓)动态创建Drawable selector
  8. 环形进度条简单实现
  9. Android——RecyclerView根据不同的item
  10. 自定义控件其实很简单(笔记一)