定制多边形View
继承View类,利用onDraw()方法中的Canvas对象绘制多边形。
其中使用的Paint,代表画笔,可以设置画笔的颜色和着色的方式(填充或者边框)。使用Path设定多边形的路径点。最后用canvas.drawPath(path,paint)方法将多边形在canvas画布上画出来。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
Path path = new Path(); //使用path绘制多边形
path.moveTo(0,0);//多边形起点为view的左上顶点
path.lineTo(getWidth(), 0);//右顶点
path.lineTo(getWidth() - getWidth()/10, getHeight()/2);
path.lineTo(getWidth(),getHeight());
path.lineTo(0,getHeight());//终点
path.close();//使这些点构成封闭的多边形
canvas.drawPath(path,paint);
//边框
paint.setColor(Color.GREEN);
paint.setStrokeWidth(4.0f);
paint.setStyle(Paint.Style.STROKE);
path.moveTo(0,0);//多边形起点为view的左上顶点
path.lineTo(getWidth(), 0);//右顶点
path.lineTo(getWidth() - getWidth()/10, getHeight()/2);
path.lineTo(getWidth(),getHeight());
path.lineTo(0,getHeight());//终点
path.close();//使这些点构成封闭的多边形
canvas.drawPath(path,paint);
}
添加View手势处理逻辑
View类中的onTouchEvent方法,对触屏事件的反馈都在此方法中回调。
@Override
public boolean onTouchEvent(MotionEvent event) {
mGD.onTouchEvent(event);
return true;
}
//触摸事件MotionEvent通常有以下几种类型事件需要处理
ACTION_DOWN //按下
ACTION_UP //抬起
ACTION_MOVE //移动
ACTION_CANCEL//取消
不过由于我们要处理抛掷的效果,所以将event传递给了mGD处理。mGD是Android中的手势探测器GestureDetector,GestureDetector需要传入一个OnGestureListener监听器。在监听器中重写相关手势的逻辑处理,这里我用到了onFling抛掷和onScroll滚动。抛掷和拖动View的效果在这两个方法中完成。
mGD = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
private float direction;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 有抛掷的情况下,计算抛掷效果
if (Math.abs(velocityY) > FLING_MIN_VELOCITY) {
float offset = velocityY * 0.5f;
// 规避fling速度方向相反问题
if ((direction > 0 && offset < 0)
|| (direction < 0 && offset > 0)) {
offset = -offset;
}
if (getY() + offset < 0) {//不要超过顶端
offset = -getY();
} else if (getY() + getHeight() + offset > screenHeight) {//不要超过底端
offset = screenHeight - getY() - getHeight();
}
offsetTopAndBottom((int) offset);
}
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float offset = e2.getY() - e1.getY(); //offset为movie-down的值,与Y轴方向相反
if (getY() + offset < 0) {//不要超过顶端
offset = -getY();
} else if (getY() + getHeight() + offset > screenHeight) {//不要超过底端
offset = screenHeight - getY() - getHeight();
}
direction = offset;
offsetTopAndBottom((int) offset);
return true;
}
});
View的移动通过offsetTopAndBottom((int) offset)方法在上下偏移给定的值,同样View也有offsetLeftAndRight方法完成左右偏移。
在onScroll中,每一次Move事件都会传入。e1是初始down事件,e2是每次move事件。e2.getY() - e1.getY()即为在Y轴上View每次移动的偏移值,其中再对超出屏幕的情况做以处理,得到即为最终偏移值。
在onFling中,传入的e1和e2几乎为同一个对象,他们Y轴的位置多数情况相同,偶尔偏差也非常小。选取的是velocityY在Y轴抛掷的速度计算偏移值。velocityY会根据手指移动的快慢相应变化。产生onFling回调时也会在回调onFling之前回调onScroll,为了减少对拖动效果的影响,设定Math.abs(velocityY) > FLING_MIN_VELOCITY抛掷速度大于给定的最小值500时,做抛掷处理。由于抛掷velocityY是在很短时间发生,按m/s计算的速度会很大,不适宜用作偏移值,故取了0.5倍offset = velocityY * 0.5f。velocityY经常与手指的抛掷方向相反,所以使用拖动时的方向来矫正。
// 规避fling速度方向相反问题
if ((direction > 0 && offset < 0)
|| (direction < 0 && offset > 0)) {
offset = -offset;
}
再过滤掉超出屏幕的情况,最终为View需要偏移的位置。
这是最基本的为自定义View增加拖动和抛掷处理逻辑,在每一个步骤都还可以扩散增加一些个性的处理效果。