Android 高級UI 目錄
Path工具類:用來記錄線條的軌跡路徑
canvas.draw(path,paint);
貝塞爾曲線
現(xiàn)實生活當中,任何的曲線和曲面都可以用貝塞爾公式來解決。
比如:
1.設計貝塞爾曲線或者貝塞爾曲線效果圖
2.怎么去得到貝塞爾曲線的幾個要素(比如二階貝塞爾:p0初始位置,p1拐點,p2結(jié)束點)
p1拐點如何確定,可以通過工具來確定,比如Photoshop點鋼筆工具等
原理和簡單推導(以三階為例):
設P0、P02、P2是一條拋物線上順序三個不同的點。過P0和P2點的兩切線交于P1點,在P02點的切線交P0P1和P2P1于P01和P11,則如下比例成立:
這是所謂拋物線的三切線定理。
當P0,P2固定,引入?yún)?shù)t,令上述比值為t:(1-t),即有:
t從0變到1,第一、二式就分別表示控制二邊形的第一、二條邊,它們是兩條一次Bezier曲線。將一、二式代入第三式得:
當t從0變到1時,它表示了由三頂點P0、P1、P2三點定義的一條二次Bezier曲線。
并且表明:
這二次Bezier曲線P02可以定義為分別由前兩個頂點(P0,P1)和后兩個頂點(P1,P2)決定的一次Bezier曲線的線性組合**。
依次類推,
由四個控制點定義的三次Bezier曲線P03可被定義為分別由(P0,P1,P2)和(P1,P2,P3)確定的二條二次Bezier曲線的線性組合,由(n+1)個控制點Pi(i=0,1,...,n)定義的n次Bezier曲線P0n可被定義為分別由前、后n個控制點定義的兩條(n-1)次Bezier曲線P0n-1與P1n-1的線性組合:
由此得到Bezier曲線的遞推計算公式
這就是這就是de Casteljau算法,可以簡單闡述三階貝塞爾曲線原理。
Bézier curve(貝塞爾曲線)是應用于二維圖形應用程序的數(shù)學曲線。 曲線定義:起始點、終止點(也稱錨點)、控制點。通過調(diào)整控制點,貝塞爾曲線的形狀會發(fā)生變化。 1962年,法國數(shù)學家Pierre Bézier第一個研究了這種矢量繪制曲線的方法,并給出了詳細的計算公式,因此按照這樣的公式繪制出來的曲線就用他的姓氏來命名,稱為貝塞爾曲線。
以下公式中:B(t)為t時間下 點的坐標;
P0為起點,Pn為終點,Pi為控制點
一階貝塞爾曲線(線段):
意義:由 P0 至 P1 的連續(xù)點, 描述的一條線段
二階貝塞爾曲線(拋物線):
原理:由 P0 至 P1 的連續(xù)點 Q0,描述一條線段。
由 P1 至 P2 的連續(xù)點 Q1,描述一條線段。
由 Q0 至 Q1 的連續(xù)點 B(t),描述一條二次貝塞爾曲線。
經(jīng)驗:P1-P0為曲線在P0處的切線。
三階貝塞爾曲線:
通用公式:
高階貝塞爾曲線:
4階曲線:
5階曲線:
二階貝塞爾曲線
public class WaveView extends View {
private Paint paint;
private Path path;
public WaveView(Context context) {
super(context);
}
public WaveView(Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
path = new Path();
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//第一個點--起始點
path.moveTo(100, 400);
//二階貝塞爾曲線1
path.quadTo(250, 200, 400, 400);
//二階貝塞爾曲線2(后面的曲線的起始點默認是接著上一個曲線的結(jié)束點)
path.quadTo(550, 600, 700, 400);
//關閉路徑(將起點和終點閉合)
path.close();
canvas.drawPath(path, paint);
}
}
三階貝塞爾曲線
path.moveTo(100, 700);
path.cubicTo(50, 500, 550, 500, 700, 700);
canvas.drawPath(path, paint);
波浪實例
public class WaveView extends View {
private Paint paint;
private Path path;
//波長
private int waveLength = 800;
private int dx;
private int dy;
public WaveView(Context context) {
super(context);
}
public WaveView(Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
path = new Path();
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
int originY = 500;
// if(dy<originY + 150){
// dy += 30;
// }
int halfWaveLength = waveLength / 2;
path.moveTo(-waveLength + dx, originY - dy);
//屏幕寬度里面畫多少哥波長
for (int i = -waveLength; i < getWidth() + waveLength; i += waveLength) {
//二階貝塞爾曲線1
/**
* 相對于起始點的增量
*/
path.rQuadTo(halfWaveLength / 2, -150, halfWaveLength, 0);
path.rQuadTo(halfWaveLength / 2, 150, halfWaveLength, 0);
}
//顏色填充
//畫一個封閉的空間
path.lineTo(getWidth(), getHeight());
path.lineTo(0, getHeight());
path.close();
canvas.drawPath(path, paint);
}
public void startAnimation() {
ValueAnimator animator = ValueAnimator.ofInt(0, waveLength);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
//無限循環(huán)
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}
調(diào)用
WaveView waveView = findViewById(R.id.waveView);
waveView.startAnimation();