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

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

这里我用ScrollView的相关源码进行学习,展示EdgeEffectCompat是如何应用到View中的。 1. 在View的绘制过程中,通过EdgeEffectCompat.setSize(width,height)和EdgeEffectCompat.draw(canvas)两个方法来绘制效果图。相关的详细解释看注释:
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()); } } } } }Android的边缘效应的相关类EdgeEffectCompat学习就到这里。
新闻热点
疑难解答