SlidingDrawer源码分析
来源:互联网 发布:宝宝学汉字软件 编辑:程序博客网 时间:2024/06/10 07:16
一属性变量分析
构造函数完成获取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: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);
- }
(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();
- }
(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)
- }
- }
- }
(2)判断是否使用单击动画,可以不使用,可以使用。
(3)animateToggle()方法,单击后的动画效果
(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函数
- }
- }
- 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);
- }
(-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)
- }
- }
(--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 {
- ......
- }
- }
(--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()处理。
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);
- }
- 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;
- }
- }
- }
0 0
- SlidingDrawer源码分析
- SlidingDrawer源码分析
- SlidingDrawer源码
- SlidingDrawer源码
- 抽屉类SlidingDrawer源码解析
- SlidingDrawer
- SlidingDrawer
- SlidingDrawer
- SlidingDrawer
- SlidingDrawer
- SlidingDrawer
- android 抽屉类slidingdrawer源码解析及运行调试
- Android中SlidingDrawer(滑动式抽屉)效果源码 (十四)
- Android:SlidingDrawer
- SlidingDrawer教程
- SlidingDrawer属性
- SlidingDrawer用法
- 源码分析
- 软件自动化测试框架
- Unity3D 渲染路径
- JTA
- JAVA编程——常用正则表达式
- C#将dll打包到程序中的具体实现
- SlidingDrawer源码分析
- Android中selector和sharp应用
- Ubuntu 开机无法自启 fcitx
- 基于TestNG 与Selenium 的自动化测试设计与实施
- 一个C语言典型的内存泄露问题
- postgresql 致命错误: 已保留的连接位置为执行非复制请求的超级用户预留
- android之播放器
- IOKING真正无锁服务器引擎之消息引擎模块Demo(no-lock)
- scala学习之:隐式转换与隐式参数