canvas教程

Android的边缘效应的相关类EdgeEffectCompat学习

字号+ 作者:H5之家 来源:H5之家 2017-11-05 16:05 我要评论( )

Android的边缘效应的相关类EdgeEffectCompat学习Android中可以的ListView,ScrollView,RecyclerView等滑动到界面的边界的时候会出现一个半透明的颜色边 框。这

Android的边缘效应的相关类EdgeEffectCompat学习

Android中可以的ListView,ScrollView,RecyclerView等滑动到界面的边界的时候会出现一个半透明的颜色边
框。这个边框就是Android的边缘效果。主要是类EdgeEffect,EdgeEffectCompat管理。效果如下图

Android的边缘效应的相关类EdgeEffectCompat学习

1,EdgeEffectCompat类的学习 源码学习: /** * 构造一个新的对象 * * <p>Note: 对于不支持的版本,将会没有效果</p> * * @param 上下文 */ public EdgeEffectCompat(Context context) { mEdgeEffect = IMPL.newEdgeEffect(context); } /** * Set the size of this edge effect in pixels. * 设置边框的大小像素 * * @param width Effect width in pixels * @param height Effect height in pixels */ public void setSize(int width, int height) { IMPL.setSize(mEdgeEffect, width, height); } /** * Reports if this EdgeEffectCompat's animation is finished. If this method returns false * after a call to {@link #draw(Canvas)} the host widget should schedule another * drawing pass to continue the animation. * * 边缘的显示动画是否结束 * * @return true if animation is finished, false if drawing should continue on the next frame. * */ public boolean isFinished() { return IMPL.isFinished(mEdgeEffect); } /** * Immediately finish the current animation. * After this call {@link #isFinished()} will return true. * 立刻结束显示动画 */ public void finish() { IMPL.finish(mEdgeEffect); } /** * 在处理滑动的时候调用 * A view should call this when content is pulled away from an edge by the user. * This will update the state of the current visual effect and its associated animation. * The host view should always {@link android.view.View#invalidate()} if this method * returns true and draw the results accordingly. * * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to * 1.f (full length of the view) or negative values to express change * back toward the edge reached to initiate the effect. * 范围:[-1,1],是要移动的距里在View的边长的占比 * @param displacement The displacement from the starting side of the effect of the point * initiating the pull. In the case of touch this is the finger position. * Values may be from 0-1. * 范围[0,1],手指所在的位置在非移动边的位置占比 * @return true if the host view should call invalidate, false if it should not. * 如果返回为true就表示会重新刷新View。 */ public boolean onPull(float deltaDistance, float displacement) { return IMPL.onPull(mEdgeEffect, deltaDistance, displacement); } /** * Call when the object is released after being pulled. * This will begin the "decay" phase of the effect. After calling this method * the host view should {@link android.view.View#invalidate()} if this method * returns true and thereby draw the results accordingly. * 是否被释放 * @return true if the host view should invalidate, false if it should not. */ public boolean onRelease() { return IMPL.onRelease(mEdgeEffect); } /** * Call when the effect absorbs an impact at the given velocity. * Used when a fling reaches the scroll boundary. * * 吸收一个速度,当到View的边界的时候会显示相应的动画 * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller}, * the method <code>getCurrVelocity</code> will provide a reasonable approximation * to use here.</p> * * @param velocity Velocity at impact in pixels per second. * @return true if the host view should invalidate, false if it should not. */ public boolean onAbsorb(int velocity) { return IMPL.onAbsorb(mEdgeEffect, velocity); } /** * 关键方法,在View的onDraw方法中调用会显示相应的动画,在调用这个方法之前要计算相应的 * 平移,旋转的量。 * Draw into the provided canvas. Assumes that the canvas has been rotated * accordingly and the size has been set. The effect will be drawn the full * width of X=0 to X=width, beginning from Y=0 and extending to some factor 小于 * 1.f of height. * * @param canvas Canvas to draw into * @return true if drawing should continue beyond this frame to continue the * animation */ public boolean draw(Canvas canvas) { return IMPL.draw(mEdgeEffect, canvas); }

EdgeEffectCompat实现原理:

EdgeEffcetCompat的实现就是在滑动控件滑动到边界的时候在边界画一个圆弧,然后根据
滑动的位置(滑动方向的偏移量,非滑动方向坐标的位置)进行缩放平移,同时进行动画的播放。
具体如下图(蓝色部分):

Android的边缘效应的相关类EdgeEffectCompat学习

2,EdgeEffectCompat类的应用

这里我用ScrollView的相关源码进行学习,展示EdgeEffectCompat是如何应用到View中的。
1. 在View的绘制过程中,通过EdgeEffectCompat.setSize(width,height)和EdgeEffectCompat.draw(canvas)两个方法来绘制效果图。相关的详细解释看注释:

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mTopEdgeEffect != null) { final int width = getWidth() - getPaddingRight() - getPaddingLeft();//内容的宽度 final int scrollY = getScrollY();//当前滑动的量 if (!mTopEdgeEffect.isFinished()) {//动画是否已经结束 int restoreCount = canvas.save(); canvas.translate(getPaddingLeft(), Math.min(0, scrollY));//画布向右平移,如果View有向下超过0的偏移量就要再向上偏移,超过上边界的平移量 mTopEdgeEffect.setSize(width , getHeight());//设置效果的展示范围(内容的宽度,和View的高度) if (mTopEdgeEffect.draw(canvas)) {//绘制边缘效果图,如果绘制需要进行动画效果返回true ViewCompat.postInvalidateOnAnimation(this);//进行动画 } canvas.restoreToCount(restoreCount); } if (!mBottomEdgeEffect.isFinished()) { int restoreCount = canvas.save(); //下面两行代码的作用就是把画布平移旋转到底部展示,并让效果向上显示 canvas.translate(getPaddingLeft() - width, Math.max(getScrollRange(), scrollY) + getHeight()); canvas.rotate(180, width, 0); mBottomEdgeEffect.setSize(width, getHeight()); if (mBottomEdgeEffect.draw(canvas)) { ViewCompat.postInvalidateOnAnimation(this); } canvas.restoreToCount(restoreCount); } } } 在View.onTouchEvent()方法中调用EdgeEffectCompat.onPull方法详细见源码: @Override public boolean onTouchEvent(MotionEvent ev) { final int actionMark = MotionEventCompat.getActionMasked(ev); switch (actionMark) { case MotionEvent.ACTION_MOVE: final int pointIndex = ev.findPointerIndex(mActionPointerId); if (pointIndex == -1) { break; } final int y = (int) ev.getY(pointIndex); int deltaY = mLastPointY - y; if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } if (deltaY > 0) {//减去积累的量 deltaY -= mTouchSlop; } else { deltaY += mTouchSlop; } mIsBeingDragged = true; } if (mIsBeingDragged) { final int oldY = getScrollY(); final int range = getScrollRange(); final int overMode = getOverScrollMode(); boolean canOverScroll = overMode == View.OVER_SCROLL_ALWAYS || (overMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); //主要的代码 if (canOverScroll) { // 边缘效果 ensureGlows(); final int pullToY = oldY + deltaY; if (pullToY < 0) {//在顶部 mTopEdgeEffect.onPull((float) deltaY / getHeight(), ev.getX(pointIndex) / getWidth()); if (!mBottomEdgeEffect.isFinished()) { mBottomEdgeEffect.onRelease(); } } else if (pullToY > range) {//在底部 mBottomEdgeEffect.onPull((float) deltaY / getHeight(), 1.0f - ev.getX(pointIndex) / getWidth()); if (!mTopEdgeEffect.isFinished()) { mTopEdgeEffect.onRelease(); } } if (mTopEdgeEffect != null && (!mTopEdgeEffect.isFinished() || !mBottomEdgeEffect.isFinished())) { ViewCompat.postInvalidateOnAnimation(this); } } mLastPointY = y; } break; case MotionEvent.ACTION_CANCEL: if (mTopEdgeEffect != null) { mTopEdgeEffect.onRelease(); mBottomEdgeEffect.onRelease(); } break; case MotionEvent.ACTION_UP: if (mTopEdgeEffect != null) { mTopEdgeEffect.onRelease(); mBottomEdgeEffect.onRelease(); } break; } return true; } private void ensureGlows() { if (getOverScrollMode() != OVER_SCROLL_NEVER) { Context context = getContext(); if (context != null && mTopEdgeEffect == null) { mTopEdgeEffect = new EdgeEffectCompat(context); mBottomEdgeEffect = new EdgeEffectCompat(context); } } else { mTopEdgeEffect = null; mBottomEdgeEffect = null; } } 在快速滑动View的时候,View会有一定的速度到达边界,这时候就要根据到达边界的速度进行显示。
一般View是用Scroller来进行Fling动画效果的。这时候就要在View.computeScroll方法中设置。详细如下: @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int oldX = getScrollX(); int oldY = getScrollY(); int newX = mScroller.getCurrX(); int newY = mScroller.getCurrY(); // Log.i(TAG, "computeScroll: oldY : " + oldY +" newY : "+newY); if (oldX != newX || oldY != newY) { final int range = getScrollRange(); final int overMode = getOverScrollMode(); boolean canOverScroll = overMode == View.OVER_SCROLL_ALWAYS || (overMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0); overScrollByCompat(newX - oldX, newY - oldY, oldX, oldY, 0, range, 0, 0); if (canOverScroll) {//这部分是吓死边缘效果的 ensureGlows(); if (newY < 0 && oldY > 0) {//到达顶部,吸收速度 mTopEdgeEffect.onAbsorb((int) mScroller.getCurrVelocity()); } else if (newY > range && oldY < range) {//到达底部,吸收速度 mBottomEdgeEffect.onAbsorb((int) mScroller.getCurrVelocity()); } } } } }

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • Android canvas.drawBitmap()画出来的图片是按dp而不是像素?

    Android canvas.drawBitmap()画出来的图片是按dp而不是像素?

    2017-10-30 10:00

  • Android Canvas编程:对rotate()和translate()两个方法的研究

    Android Canvas编程:对rotate()和translate()两个方法的研究

    2017-10-29 11:40

  • 对Canvas和Matrix的理解

    对Canvas和Matrix的理解

    2017-10-15 10:00

  • android浏览器,使用canvas绘制图像重影的解决办法

    android浏览器,使用canvas绘制图像重影的解决办法

    2017-10-14 15:11

网友点评