本來前天應該整理的了,但是臨時有任務給耽擱了,不過今天回過頭來發現好多注意點都忘了,還是記錄一下吧。
canvas繪製點,繪製線,繪製各種圖形,其實這個可以說是最簡單的了,也沒有什麼具體可以講的,方法的參數含義基本上都可以理解,這裏只說一點:圓弧或者扇形是的繪製如何確定。圓弧的繪製是這樣的,他是將一個矩形(之所以不是正方形是因爲也可以是橢圓的一部分)的內切圓的一部分截取出來的。那問題就是矩形如何確定呢?這裏舉個例子:例如所需扇形的半徑爲100,圓心座標爲(200,200),那麼所需矩形的左上角座標就爲(100,100),右下角座標爲(300,300),我想規律已經出來了,沒錯就是這麼算的,矩形的左上角座標x=centerX-r,y=centerY-r ,矩形右下角座標爲 x=centerX+r y=centerY+r 。這個自己敲一下代碼就看出來了。。。。其他方法,沒什麼好說的,自己敲一下代碼就理解了,不多說了也,我把我的代碼複製下來,裏面也有注意的地方。
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.YELLOW);
canvas.drawPoint(50,50,mPaint);
//實心圓 圓心座標x y 半徑 r 畫筆
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(100, 100, 80, mPaint);
//繪製圓弧(實心) 這裏注意如何確定參數中第一個矩形的座標如何確定,(centerX-r,centerY-r,centerX+r,centerY+r) 開始的角度 -90度爲我們理解的0度方向 轉過的角度 是否連接圓心
RectF oval = new RectF(0, 200, 200, 400);
// canvas.drawArc(oval,-90,90,false,mPaint);//圓弧
canvas.drawArc(oval, -90, 90, true, mPaint);//扇形
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
RectF ov = new RectF(0, 350, 200, 550);
// canvas.drawArc(ov,-90,90,true,mPaint);//扇形
canvas.drawArc(ov, -90, 90, false, mPaint);//弧線
//橢圓
canvas.drawOval(0, 650, 200, 800, mPaint);
//矩形
Rect rect = new Rect(0, 850, 200, 1050);
canvas.drawRect(rect, mPaint);
//圓角矩形
canvas.drawRoundRect(250, 10, 450, 210, 15, 15, mPaint);
// 按照path寫文字
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL);
String te = "abcdefsgeg";
Path pa = new Path();
pa.addArc(300, 500, 900, 1000, -180, 120);
canvas.drawTextOnPath(te, pa, 0, 0, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
Path p = new Path();
p.moveTo(500, 800);
p.quadTo(650, 850, 450, 1150);//賽貝爾曲線
// p.cubicTo(100,200,200,500,6200,850);//兩個控制點的賽貝爾曲線 http://www.cnblogs.com/lenve/p/5865874.html
canvas.drawPath(p, mPaint);
補充:看來確實是過了兩天忘了很多東西,有兩個重要的知識忘了記錄了
1.繪製線,必須要說Paint中與線有關的參數,因爲很重要,很重要
//繪製線的一些設置
mPaint.setStrokeCap(Paint.Cap.SQUARE);//Paint.Cap.BUTT 無延伸效果,是多大就多大 Paint.Cap.ROUND 延伸爲圓角 Paint.Cap.SQUARE 延伸爲矩形
mPaint.setStrokeJoin(Paint.Join.ROUND);//設置連接處效果 Paint.Join.BEVEL 有倒角,爲直線倒角 Paint.Join.MITER 沒有倒角 Paint.Join.ROUND 圓形倒角 詳見:http://blog.csdn.net/abcdef314159/article/details/51720686
mPaint.setStrokeMiter(Paint.ANTI_ALIAS_FLAG);//設置畫筆的傾斜度(不知有沒有用)
mPaint.setHinting(Paint.HINTING_OFF);//設置畫筆的隱藏模式 Paint.HINTING_OFF 關閉 Paint.HINTING_ON 打開
//TODO:特殊線需要單獨研究
mPaint.setPathEffect(new CornerPathEffect(2));//有時候我們需要點畫線等特殊的線,這個可以設置連接線的形狀 CornerPathEffect 兩段線之間用圓角 DashPathEffect將線段虛線話 DiscretePathEffect打散path效果 其他效果詳見 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0120/2334.html
//mPaint.setRasterizer();//設置光柵,被廢棄(無需研究)
下面舉例說明兩個參數的含義,其他可以看看裏面引用的參考文章,講的很好
mPaint.setStrokeCap(Paint.Cap.BUTT);//設置線的兩端情況 默認爲此狀態即是多長就多長
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(25);
Path path=new Path();
path.moveTo(400,500);
path.lineTo(600,500);
canvas.drawPath(path,mPaint);
mPaint.setStrokeCap(Paint.Cap.ROUND);//兩端爲圓角伸出
Path path1=new Path();
path1.moveTo(400,550);
path1.lineTo(600,550);
canvas.drawPath(path1,mPaint);
mPaint.setStrokeCap(Paint.Cap.SQUARE);//兩端爲矩形伸出
Path path2=new Path();
path2.moveTo(400,600);
path2.lineTo(600,600);
canvas.drawPath(path2,mPaint);
mPaint.setStrokeCap(Paint.Cap.BUTT);
mPaint.setStrokeJoin(Paint.Join.BEVEL);//設置線與線的連接方式,此參數表示直切,還有圓角,直角,默認爲直角
Path path3=new Path();
path3.moveTo(400,650);
path3.lineTo(600,650);
path3.lineTo(600,750);
canvas.drawPath(path3,mPaint);
效果就是最後第二張圖片中的幾條大粗線。
2.繪製點線面,不得不說一個path函數,非常非常重要 參見:http://www.cnblogs.com/lenve/p/5865874.html
下面開始繪製一個大家都會練習的一個例子:時鐘。裏面有很重要的內容,最好運行一下,仔細看思路。
public class TimeView extends TextView {
private Paint mPaint;
private int mSecond;
private int mMinite;
private int mHour;
public TimeView(Context context) {
this(context, null);
}
public TimeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
String time = getTimeShort();
int hour = Integer.valueOf(time.substring(0, 2));
mHour = hour % 12;
mMinite = Integer.valueOf(time.substring(3, 5));
mSecond = Integer.valueOf(time.substring(6, time.length()));
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
mSecond++;
if (mSecond == 60) {
mSecond = 0;
mMinite++;
if (mMinite == 60) {
mMinite = 0;
mHour++;
if (mHour == 12) {
mHour = 0;
}
}
}
invalidate();
break;
}
}
};
@Override
protected void onDraw(Canvas canvas) {
int screenX = getMeasuredWidth() / 2;
int screenY = getMeasuredHeight() / 2;
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
mPaint.setColor(Color.GREEN);
//繪製背景
canvas.drawColor(Color.YELLOW);
//繪製大圓
canvas.drawCircle(screenX, screenY, 250, mPaint);
//繪製表芯小圓
mPaint.setColor(Color.GRAY);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(screenX, screenY, 18, mPaint);
//繪製指針小圓
mPaint.setColor(Color.GREEN);
canvas.drawCircle(screenX, screenY, 10, mPaint);
//繪製文字北京時間 問題:如何計算指定長度的字符所佔的角度(未解決)
mPaint.setStrokeWidth(2);
String s = "北京時間";
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 19));
Path textPath = new Path();
RectF rect = new RectF(screenX - 205, screenY - 205, screenX + 205, screenY + 205);
textPath.arcTo(rect, -111, 90, false);//這裏沒有做適配,所以可能運行不是在文字正上方
canvas.drawTextOnPath(s, textPath, 0, 0, mPaint);
//繪製刻度 這個不是很好理解:思路是這樣的,首先將畫布平移到圓的中心(本例子中就是屏幕中央),那麼此時高中立體幾何的知識就用到了,畫布已經平移到屏幕中央了,那麼
// 此時屏幕中央的座標是多少呢?答案是(0,0),屏幕左上角的座標是多少呢(-screenWidth/2,-screenHeight/2),爲什麼?想想立體幾何中的知識(具體什麼知識我也忘了,不過確實是)
//然後我們繪製刻度線就要以屏幕中央爲基準點,繪製。怎麼繪製?很簡單,畫筆只負責在同一個位置畫直線,然後讓畫布旋轉,相對運動嗎,這樣轉一圈,刻度就好了
//現在是怎麼確定直線兩點座標爲題,我的思路是在正上方繪製直線,那麼座標就是多少呢?例如線長爲20,圓半徑爲100,則座標爲(0,-100)和(0,-120).理解一下,再讀讀上面內容畫個座標系就懂了
canvas.save();//保存當前畫布的各種屬性狀態,配合canvas.restore();使用
canvas.translate(screenX, screenY);
for (int i = 0; i < 60; i++) {
if (i % 5 == 0) {//長
mPaint.setStrokeWidth(3);
canvas.drawLine(0, -250, 0, -274, mPaint);
} else {//短
mPaint.setStrokeWidth(2);
canvas.drawLine(0, -250, 0, -265, mPaint);
}
canvas.rotate(6);
}
//繪製數字(思路和繪製刻度一樣)
Rect re = new Rect();
for (int i = 1; i < 13; i++) {
int t = i - 1;
if (t == 0) {
t = 12;
}
mPaint.setStrokeWidth(2);
mPaint.setTextSize(DensityUtils.sp2px(getContext(), 16));
String num = String.valueOf(t);
mPaint.getTextBounds(num, 0, num.length(), re);
canvas.drawText(num, -re.width() / 2, -280, mPaint);
canvas.rotate(30);
}
canvas.restore();//回覆保存的畫布各種屬性狀態,這中間做的各種改變都放棄掉
canvas.save();
//繪製時針
mPaint.setStrokeWidth(7);
canvas.translate(screenX, screenY);
canvas.rotate((float) 30 * mHour + 30 * (float) mMinite / 60);
canvas.drawLine(0, 20, 0, -155, mPaint);
canvas.restore();
//繪製分針
canvas.save();
mPaint.setStrokeWidth(5);
canvas.translate(screenX, screenY);
canvas.rotate((float) 6 * mMinite + 6 * (float) mSecond / 60);
canvas.drawLine(0, 25, 0, -175, mPaint);
canvas.restore();
canvas.save();
//繪製秒針
canvas.translate(screenX, screenY);
mPaint.setStrokeWidth(3);
canvas.rotate(6 * mSecond);
canvas.drawLine(0, 35, 0, -190, mPaint);
canvas.restore();
mHandler.sendEmptyMessageDelayed(0, 1000);
}
/**
* 獲取時間 小時:分;秒 HH:mm:ss
*
* @return
*/
public static String getTimeShort() {
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
Date currentTime = new Date();
return formatter.format(currentTime);
}
public void onDestory() {
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
}