在写OverScroller使用(自定义一个ScrollView)的文章时,觉得EdgeEffect的使用有必要单独提出来做说明,主要是在一些可以滑动或放缩的自定义ViewGroup类控件中经常用到,并且是用来提示用户已经滑动到边界的。比较常见的例子就是ScrollView,当滑动到边界时就会在边界延申出一部分颜色弧形边块。本文代码例子(OverScroller使用)中就是使用EdgeEffect类来实现滑动边界提醒的。
git项目: https://gitee.com/guaishoun/over_scroller_edge_effect.git
EdgeEffect英文直译就边界效果,用于在可滑动的控件中提示用户已经滑动到边界了。EdgeEffect是通过控制边界状态来控制绘制的,主要状态有拉onPull、碰撞速度吸收onAbsorb、释放onRelease这三种状态,然后在绘制中传入canvas绘制边界效果,其中控制状态主要还是根据onTouchEvent触摸事件处理来决定。所以分三步使用EdgeEffect:初始化配置、状态控制、绘制。
主要关注拉onPull、碰撞速度吸收onAbsorb、释放onRelease这三种状态及绘制draw。
onPull
/**
* 拉状态
* deltaDistance 高度颜色深浅,范围0-1,注意是比例,比例越大边界效果越完整,颜色越深
* displacement 效果弧形的中心点,范围0-1,中心点位于设置的宽度的位置
**/
public void onPull(float deltaDistance, float displacement);
onAbsorb
/**
*撞击边界时的状态,在配合fling甩动使用,惯性滑动到边界时吸收速度显示边界效果
* Velocity 撞击时剩余的速度
*/
public void onAbsorb(int velocity);
onRelease
/**
* 当拉状态结束时,使用释放状态
*/
public void onRelease()
draw
/**
*传入canva绘制边界效果
* 1 要放在最后绘制,比如放到GroupView的onDrawForeground(Canvas canvas)中绘制
* 2 默认是绘制在未变换坐标上画的,也就是控件top位置,如果要画到其他位置需要自己的变换坐标来绘制
* 3 效果没有finish的话,自己需要Invalidate()触发重画
**/
public boolean draw(Canvas canvas)
...
public OverScrollerEdgeEffectView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mScroller = new OverScroller(context);
ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop = vc.getScaledTouchSlop()*0.8f;
mMinimumVelocity = vc.getScaledMinimumFlingVelocity();
mMaximumVelocity = vc.getScaledMaximumFlingVelocity();
//定义上下边界和颜色
edgeEffectTop = new EdgeEffect(context);
edgeEffectTop.setColor(Color.DKGRAY);
edgeEffectBottom = new EdgeEffect(context);
edgeEffectBottom.setColor(Color.GREEN);
}
....
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
//设置宽高,可以放到其他位置
edgeEffectTop.setSize(w,h);
edgeEffectBottom.setSize(w,h);
}
...
onTouchEvent中处理拉及释放
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction()&MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
//恢复
edgeEffectTop.finish();
edgeEffectBottom.finish();
postInvalidateOnAnimation();
break;
case MotionEvent.ACTION_MOVE:
...
//下拉状态
if(getScrollY()+v< 0 ){
edgeEffectTop.onPull(Math.abs(v/getHeight()),event.getX()/getWidth());
}
if(getScrollY()+getHeight()+v> containHeight){
edgeEffectBottom.onPull(Math.abs(v/getHeight()),1f-event.getX()/getWidth());
}
postInvalidateOnAnimation();
....
break;
case MotionEvent.ACTION_UP:
....
//释放
edgeEffectTop.onRelease();
edgeEffectBottom.onRelease();
postInvalidateOnAnimation();
break;
default:
break;
}
return true;
}
computeScroll 撞击吸收
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
int y = mScroller.getCurrY();
scrollTo(0,y);
//Absorb Velocity
if(edgeEffectTop.isFinished() && getScrollY()< 0 ){
edgeEffectTop.onAbsorb((int) mScroller.getCurrVelocity());
}
if(edgeEffectBottom.isFinished() && getScrollY()+getHeight()> containHeight){
edgeEffectBottom.onAbsorb((int) mScroller.getCurrVelocity());
}
postInvalidateOnAnimation();
}
}
@Override
public void onDrawForeground(Canvas canvas){
super.onDrawForeground(canvas);
//绘制上边界
if(!edgeEffectTop.isFinished()){
canvas.save();
edgeEffectTop.draw(canvas);
canvas.restore();
invalidate();
}
//绘制下边界,要自己变换坐标哟
if(!edgeEffectBottom.isFinished()){
canvas.save();
canvas.translate(-getWidth(),0);
canvas.rotate(180,getWidth(),0);
canvas.translate(0,-containHeight);
edgeEffectBottom.draw(canvas);
canvas.restore();
invalidate();
}
}
通过上面的介绍,可以发现边界效果EdgeEffect封装了边界绘制效果,使用方法主要为初始化配置、控制状态和绘制三个步骤,抓住这三个步骤就很容易理解和使用边界效果了。