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


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

边缘效应效果图

1,EdgeEffectCompat类的学习
  1. 源码学习:
/**     * 构造一个新的对象     *     * 

Note: 对于不支持的版本,将会没有效果

* * @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的边界的时候会显示相应的动画 *

When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller}, * the method getCurrVelocity will provide a reasonable approximation * to use here.

* * @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); }
  1. EdgeEffectCompat实现原理:

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

    原理图
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);            }        }    }
  1. 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;        }    }
  1. 在快速滑动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());                    }                }            }        }    }

Android的边缘效应的相关类EdgeEffectCompat学习就到这里。

更多相关文章

  1. Android(安卓)Activity 界面跳转动画(系统、自定义)
  2. android SeekBar
  3. ImageButton设置 android:background添加点击效果
  4. android手势操作滑动效果触摸屏事件处理
  5. android手势操作滑动效果触摸屏事件处理
  6. Android(安卓)嵌套滑动机制(NestedScrolling)
  7. Android实现导航菜单左右滑动效果
  8. Android实现左右滑动指引效果
  9. Android开发学习之Animation之Android帧动画解析

随机推荐

  1. Android(安卓)开发中常见的Eclipse排版设
  2. 【Android】在Android上使用OrmLite数据
  3. 两种特殊TabHost实现
  4. android make 命令使用
  5. Android开发之道(3)系统演进历史
  6. Android提高第十八篇之自定义PopupWindow
  7. android介绍以及学习方法
  8. Android am 指令的使用
  9. android获取设备唯一标识完美解决方案
  10. C#/mono开发Android应用程序入门(二)-第一