这里主要讲Path的填充方式 FillType 和 他的一个辅助工具类 PathMeasure
前文我们已经讲过如何用Path画出各种图形,《Android:视图绘制(三) ——Path介绍》,不了解的朋友可以移步。
FillType 填充方式
前文讲Paint的时候,我们就讲到过填充方式,不记得的朋友请移步《Android:视图绘制(一) ——基本的绘图操作Paint和Canvas》,Path的填充方式和Paint的不同,他提供了四种可供选择的值。
· FillType.WINDING: 默认值,取Path的所有区域
· FillType.EVEN_ODD: 取path所在并不相交的区域
· FillType.INVERSE_WINDING: 取path的外部区域
· FillType.INVERSE_EVEN_ODD: 取path外部和相交区域
INVERSE 相反取逆的意思,所以下面的两种填充方式是上面两种的相反形式。
Path提供了 setFillType(FillType ft)
方法,来设置填充方式。下面看图:
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.
值得注意的是,当我们用了INVERSE 属性,取相交外部区域,会填充整个Canvas。
FillType系列还有一些函数。
boolean isInverseFillType() 是否是INVERSE 系列函数。
void toggleInverseFillType() 切换到相反的函数。即 WINDING 切换到 INVERSE_WINDING,反之亦然。
PathMeasure Path的辅助工具类,用于Path的计算的。其可以获得Path的长度和其中任意点的坐标,基于此,我们多是实现一些沿特定图形运动的动画。
其提供了两个构造方法。
PathMeasure()
PathMeasure(Path path, boolean forceClosed)
一种是无参的,另一种提供了两个参数。
参数一 path:因为PathMeasure是用于Path的计算的,所以PathMeasure一定要和我们要操作的Path绑定到一起,其内部会有一个全局的变量用于保存我们的Path,这个构造函数就是一个赋值的过程。
参数二 forceClosed:强制关闭,是一个Boolean的变量。官方解释:If true, then the path will be considered as “closed” even if its contour was not explicitly closed. 就是说,如果我们设置为true的话,就相当于强行的把Path闭合了,也就是相当于调用了Path的close。
PathMeasure 还提供了一个方法setPath(Path path, boolean forceClosed)
可以看到其参数和构造方法中的一样,其实就是对应上面那个无参的构造方法,用来设置值的。
下面来看一下 PathMeasure 中的主要方法。
float getLength() 返回Path的总长度。
boolean getPosTan(float distance, float pos[], float tan[]) 获得距Path起点distance长度的点的坐标,并赋值给pos[]
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 获得距Path起点startD到stopD长度的path,并赋值给dst
还记得前面讲的贝塞尔曲线吗,今天用这个贝塞尔曲线加PathMeasure ,给大家模拟一个小球落地的例子:
Image may be NSFW.
Clik here to view.
Gif效果不是很好,有兴趣的可以拷到自己程序中看效果。直接上代码,注释很详细,就不在赘述了。
/**
* 模拟小球抛物线落地 Demo
*
* @author adong
* @date 2016-9-24 17:11:39
*/
public class CustomPaintView extends View {
private Paint mPaint;
private Path mPath;
private PathMeasure pathMeasure;
// path长度
private int mLenght;
// 当前距离
private int mCurrentPath;
// 当前点坐标
private float[] currentPosition;
// 用于存放小球的Bitmap
private Bitmap bitmap;
public CustomPaintView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mPaint = new Paint();
mPath = new Path();
currentPosition = new float[2];
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.leida_point_small);
mPaint.setColor(Color.parseColor("#ff0000"));
mPaint.setStyle(Paint.Style.STROKE);
mPath.moveTo(0, 100);
// 贝塞尔曲线,用于模拟抛物线
mPath.cubicTo(300, 50, 600, 150, 800, 500);
mPath.quadTo(950, 400, 1000, 500);
pathMeasure = new PathMeasure();
pathMeasure.setPath(mPath, false);
// 获得长度
mLenght = (int) pathMeasure.getLength();
}
public CustomPaintView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomPaintView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
if (mCurrentPath == 0) {
// 第一次启动
startAnimator();
} else {
// 在每次ValueAnimator的回调中更新小球的位置
canvas.drawBitmap(bitmap, currentPosition[0] - bitmap.getWidth() / 2,
currentPosition[1] - bitmap.getHeight() / 2, mPaint);
}
}
/**
* 计算每次的位置
*/
private void startAnimator() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, mLenght);
// 持续时间
valueAnimator.setDuration(5000);
// 加速插值器
valueAnimator.setInterpolator(new AccelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 获得当前的长度
mCurrentPath = (int) animation.getAnimatedValue();
// 获得对应点坐标
pathMeasure.getPosTan(mCurrentPath, currentPosition, null);
// 重绘
invalidate();
}
});
valueAnimator.start();
}
}