1.介紹
- 先看一下效果圖
用過電腦版印象筆記演示功能的應該知道這個效果,現在想在手機上實現一下,當然這個是最終的目標,這篇只是介紹實現過程中的一步 —— 繪製一條寬度逐漸變大的路徑
2.實現
2.1完成圖
2.2思路
把一條路徑分成很多段,每段path首尾相連,然後依次設置每一段path的寬度,把這些path存儲在一個集合中,最後依次繪製出來。
這種方法實現的問題主要是如果截取的段太少,在拐角處顯示不圓滑,鋸齒嚴重,所以定義了DEFAULT_SEGMENT_LENGTH來控制截取長度,長度越小,鋸齒度越小,拐角處越圓滑。對於這個問題,如有更好的思路,請告訴我。
2.3把一條path分成多段path的方法
這裏要使用到PathMeasure,PathMeasure的getSegment方法就是獲取一段path,
public boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
startD 表示從path某個長度位置開始
stopD 表示某個長度位置結束
dst 表示截取後的path
startWithMoveTo 表示這個截取的path開始位置是否移動到截取的開始位置,false的話,path開始點位置是(0,0),這裏需要設置爲true,不然達不到首尾相連的效果
對於path長度的獲取,PathMeasure有相應的方法getLength();
2.4截取路徑的方法實現
/**
* 截取path
* @param path
*/
private void getPaths(Path path){
PathMeasure pm = new PathMeasure(path, false);
float length = pm.getLength();
int segmentSize = (int) Math.ceil(length / DEFAULT_SEGMENT_LENGTH);
Path ps = null;
PathSegment pe = null;
int nowSize = pathSegments.size();//集合中已經有的
if(nowSize == 0){
ps = new Path();
pm.getSegment(0, length, ps, true);
pe = new PathSegment(ps);
pe.setAlpha(255);
pe.setWidth(DEFAULT_WIDTH);
pathSegments.add(pe);
} else{
for (int i = nowSize; i < segmentSize; i++) {
ps = new Path();
pm.getSegment((i - 1) * DEFAULT_SEGMENT_LENGTH - 0.4f, Math.min(i * DEFAULT_SEGMENT_LENGTH, length), ps, true);
pe = new PathSegment(ps);
pe.setAlpha(255);
pe.setWidth((float) Math.min(MAX_WIDTH, i * 0.3 + DEFAULT_WIDTH));
pathSegments.add(pe);
}
}
}
3.完整代碼
public class TailView2 extends View{
private Paint paint;
private Path mFingerPath;
private float mOriginX;
private float mOriginY;
private List<PathSegment> pathSegments;
private class PathSegment{
Path path;
float width;
int alpha;
public PathSegment(Path path) {
this.path = path;
}
public Path getPath() {
return path;
}
public void setPath(Path path) {
this.path = path;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public int getAlpha() {
return alpha;
}
public void setAlpha(int alpha) {
this.alpha = alpha;
}
}
public TailView2(Context context) {
this(context, null, 0);
}
public TailView2(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TailView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
//-------------------------------------------------
mFingerPath = new Path();
pathSegments = new ArrayList<>();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (PathSegment p: pathSegments) {
paint.setAlpha(p.getAlpha());
paint.setStrokeWidth(p.getWidth());
canvas.drawPath(p.getPath(), paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
pathSegments.clear();
mOriginX = x;
mOriginY = y;
mFingerPath.reset();
mFingerPath.moveTo(mOriginX, mOriginY);
break;
case MotionEvent.ACTION_MOVE:
getPaths(mFingerPath);
mFingerPath.lineTo(x, y);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
/**
* 越小,線條鋸齒度越小
*/
private static final float DEFAULT_SEGMENT_LENGTH = 10F;
private static final float DEFAULT_WIDTH = 3F;
private static final float MAX_WIDTH = 45F;
/**
* 截取path
* @param path
*/
private void getPaths(Path path){
PathMeasure pm = new PathMeasure(path, false);
float length = pm.getLength();
int segmentSize = (int) Math.ceil(length / DEFAULT_SEGMENT_LENGTH);
Path ps = null;
PathSegment pe = null;
int nowSize = pathSegments.size();//集合中已經有的
if(nowSize == 0){
ps = new Path();
pm.getSegment(0, length, ps, true);
pe = new PathSegment(ps);
pe.setAlpha(255);
pe.setWidth(DEFAULT_WIDTH);
pathSegments.add(pe);
} else{
for (int i = nowSize; i < segmentSize; i++) {
ps = new Path();
pm.getSegment((i - 1) * DEFAULT_SEGMENT_LENGTH - 0.4f, Math.min(i * DEFAULT_SEGMENT_LENGTH, length), ps, true);
pe = new PathSegment(ps);
pe.setAlpha(255);
pe.setWidth((float) Math.min(MAX_WIDTH, i * 0.3 + DEFAULT_WIDTH));
pathSegments.add(pe);
}
}
}
}