轉載請註明:http://blog.csdn.net/duguang77/article/details/40869079
前面幾篇博客,簡單介紹了一下Canvas+Path+Paint的API,下面我們通過這幾個類中的方法畫出下面的效果圖的樣式
Demo下載地址:https://github.com/z56402344/Android_Graphics_Instance_One
效果圖:
動態效果圖:
這樣的圖在做項目的時候,一般是不會讓美工去切圖的,一是麻煩,二是沒有辦法去做好適配,所以大家只能通過繪圖類進行繪製了
我們先來看下這個效果圖最難的點怎麼畫.
這張效果圖最難的點,我個人認爲就是圓上的箭頭怎麼指向某一個柱狀體頂點中間位置
圖好像看起來還蠻複雜的,其實這些都是假象,我們先來拆分下圖層吧
效果拆分層
簡化圖
這樣拆分出來的圖,大家就應該知道這張圖示怎麼畫的吧!
我們來細講一下,圓心點座標我們通過
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = canvas.getWidth();
mHeight = canvas.getHeight();
mCenterX = mWidth/2;
mCenterY = mHeight/4;
}
繼承的View類 OnDraw()方法中的Canvas獲取出屏幕一半寬,1/4高的點的位置,這就是上圖中的O點座標,而柱狀體我們也是通過自己給的座標點畫出的,所以這兩個點都是已知的。
我們最重要的是控制好過圓心,畫出三角形,
我們通過之前瞭解到通過Canvas+Path+Paint組合API可以畫出三角形,但是我們並不知道點P和P'的座標位置,
//開始畫三角形
Path path = new Path();// 三角形
path.moveTo((float)(x2), (float)(y2));//P點座標
path.lineTo((float)(mPointB.x), (float)(mPointB.y));//圓心點座標
path.lineTo((float)x1, (float)y1);//P'點座標
path.close();//閉合畫筆路徑
canvas.drawPath(path, paint);//開始畫
通過簡化圖,我們可以看出,其實就是一個數學問題,通過點O座標和點H座標,OP'和OP邊長是自己給定的定值所以也是已知的,OH邊長已知,PH和P'H通過勾三股四算出可得,有了這些參數我們就可以組成一個二元一次方程組來算出P和P'座標如下所示
<span style="white-space:pre"> </span>PointBean mPointA;<span style="white-space:pre"> </span>//柱狀體頂部中心座標
<span style="white-space:pre"> </span>PointBean mPointB = new PointBean(760, 400); //初始化時,假設的一個圓心點座標
//下面公式通過圓心點座標和柱狀體頂部中心點座標,通過二元一次方程組計算出其餘兩個三角形的座標點位置
// x=x1-a*sin{arctan[(y2-y1)/(x2-x1)]}
// y=y1+a*cos{arctan[(y2-y1)/(x2-x1)]}
//求出座標點P
double x1 = mPointA.x - 50 * Math.sin(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
double y1 = mPointA.y + 50 * Math.cos(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
//求出座標點P'
double x2 = mPointA.x + 50 * Math.sin(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
double y2 = mPointA.y - 50 * Math.cos(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
這就是這圖最難的點,知道點P; P' ,H三點座標,就可以輕鬆畫出過圓心的三角形了
下面是所有代碼,之後我會把項目放到github上,大家可以去下載
/**
* 通過柱狀體頂部中心點座標和圓心點座標,畫出過圓心的三角形
* @author DuGuang
*
*/
public class CustomTrigon extends View {
PointBean mPointA; //柱狀體頂部中心座標
PointBean mPointB = new PointBean(760, 400); //初始化時,假設的一個圓心點座標
private float mCenterX; //圓心點座標X
private float mCenterY; //圓心點座標Y
private int mWidth; //畫布的寬 == 手機屏幕的寬
private int mHeight;//畫布的高 == 手機屏幕的高 - ActionBar - 頂部title
public CustomTrigon(Context context) {
super(context);
}
public CustomTrigon(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomTrigon(Context context, AttributeSet attrs) {
super(context, attrs);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = canvas.getWidth();
mHeight = canvas.getHeight();
mCenterX = mWidth/2;
mCenterY = mHeight/4;
mPointA = new PointBean((int)mCenterX, (int)mCenterY);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Style.FILL);
paint.setStrokeWidth(30f);
paint.setDither(true);
paint.setColor(getResources().getColor(R.color.cril));
getDot2(paint, canvas);
}
public void getDot2(Paint paint, Canvas canvas) {
//下面公式通過圓心點座標和柱狀體頂部中心點座標,通過二元一次方程組計算出其餘兩個三角形的座標點位置
// x=x1-a*sin{arctan[(y2-y1)/(x2-x1)]}
// y=y1+a*cos{arctan[(y2-y1)/(x2-x1)]}
//求出座標點P
double x1 = mPointA.x - 50 * Math.sin(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
double y1 = mPointA.y + 50 * Math.cos(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
//求出座標點P'
double x2 = mPointA.x + 50 * Math.sin(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
double y2 = mPointA.y - 50 * Math.cos(Math.atan((mPointB.y - mPointA.y) / (mPointB.x - mPointA.x)));
Log.i("dg", "x >>> " + x1 + " y >>> " + y1);
//開始畫三角形
Path path = new Path();// 三角形
path.moveTo((float)(x2), (float)(y2));//P點座標
path.lineTo((float)(mPointB.x), (float)(mPointB.y));//圓心點座標
path.lineTo((float)x1, (float)y1);//P'點座標
path.close();//閉合畫筆路徑
canvas.drawPath(path, paint);//開始畫
}
/**
* 通過不同等級,塞入一個柱狀體頂部中心點座標
* @param pointB
*/
public void setData(PointBean pointB){
mPointB = pointB;
invalidate();
}
}
/**
* 自定義控件圓形
* @author DuGuang
*
*/
public class CustomCircle extends View {
private float mCenterX; // 圓形X軸中心
private float mCenterY; //圓形Y軸中心
private float mCircleSize; //圓形直徑大小
private Context mContext;
private int mWidth; //畫布的寬 == 手機屏幕的寬
private int mHeight;//畫布的高 == 手機屏幕的高 - ActionBar - 頂部title
public CustomCircle(Context context) {
super(context);
init(context);
}
public CustomCircle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public CustomCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 初始化數據
* @param context
*/
private void init(Context context) {
this.mContext = context;
this.mCenterX = 350f;
this.mCenterY = 350f;
this.mCircleSize = 285f;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mWidth = canvas.getWidth();
mHeight = canvas.getHeight();
mCenterX = mWidth/2;
mCenterY = mHeight/4;
mCircleSize = mHeight/6;
//第一個畫筆,畫出一個空心圓
Paint paint = new Paint();
paint.setAntiAlias(true); //消除齒距
paint.setStyle(Style.STROKE); //空心畫筆
paint.setStrokeWidth(30f); //畫筆寬度
paint.setDither(true); //消除脫穎
paint.setColor(getResources().getColor(R.color.cril)); //設置畫筆顏色
//通過Path 用canvas在畫布上畫出圓形
Path path = new Path();
path.addCircle(mCenterX, mCenterY, mCircleSize, Path.Direction.CCW);
canvas.drawPath(path, paint);
//第二個畫筆,畫出一個實心圓
Paint paint_white = new Paint();
Path path_white = new Path();
paint_white.setAntiAlias(true);
paint_white.setStyle(Style.FILL);
paint_white.setDither(true);
paint_white.setColor(getResources().getColor(R.color.white));
// path_white.addCircle(mCenterX, mCenterY, mCircleSize-15, Path.Direction.CCW);
path_white.addCircle(mCenterX, mCenterY, 5, Path.Direction.CCW);
canvas.drawPath(path_white, paint_white);
//第三個畫筆,畫出一個空心圓
Paint paint_STROKE = new Paint();
Path path_STROKE = new Path();
paint_STROKE.setAntiAlias(true);
paint_STROKE.setStyle(Style.STROKE);
paint.setStrokeWidth(5f); //畫筆寬度
paint_STROKE.setDither(true);
paint_STROKE.setColor(getResources().getColor(R.color.cril));
path_STROKE.addCircle(mCenterX, mCenterY, mCircleSize-25, Path.Direction.CCW);
canvas.drawPath(path_STROKE, paint_STROKE);
}
}
/**
* 自定義空間,帶圓角的柱狀體
* @author DuGuang
*
*/
public class CustomRect extends View {
//圓角柱狀體4個角的值
private float[] radii = { 12f, 12f, 12f, 12f, 0f, 0f, 0f, 0f };
//柱狀體的顏色
private int[] colors = { R.color.rect_cril_leve1, R.color.rect_cril_leve2,
R.color.rect_cril_leve3, R.color.rect_cril_leve4,
R.color.rect_cril_leve5, R.color.rect_cril_leve6 };
private int mWidth; //畫布的寬 == 手機屏幕的寬
private int mHeight;//畫布的高 == 手機屏幕的高 - ActionBar - 頂部title
private int mRectWidth; //矩形寬
private int mRectHeight;//矩形高
private Paint mPaint;
private String mLevel; //畫的L1-L3 字樣
private String mName; //畫的初級,高級,專家字樣
private static float mToY = 15f; //小於1,整體往下移動;大於1,整體往上移動
private static float mRectY = 4;//往1方向,矩形長度拉長,往10方向,矩形長度縮短
private ArrayList<String> mPointList; //柱狀體頂部中心座標的集合
public CustomRect(Context context) {
super(context);
init(context);
}
public CustomRect(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public CustomRect(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPointList = new ArrayList<String>();
mWidth = canvas.getWidth();
mHeight = canvas.getHeight();
mRectWidth = (int) (mWidth / 9.5);
mRectHeight = mHeight/2;
//循環出6個柱狀體
for (int i = 0; i < 6; i++) {
initBitmaps(canvas,i);
}
}
/**
* 畫矩形
* @param canvas
* @param index
*/
private void initBitmaps(Canvas canvas,int index) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Style.FILL);
mPaint.setStrokeWidth(30f);
mPaint.setDither(true);
mPaint.setColor(getResources().getColor(colors[index]));
//通過Path路徑,計算出每個柱狀體寬高,並把柱狀體頂部中心座標放入集合
//柱狀體頂部中心座標放入集合,用於和圓心中央的三角,通過中心座標和柱狀體座標,畫出三角形
Path path = new Path();
int width = (int) (mRectWidth/2+(index*mRectWidth*1.5));
int height_top = (int) (mRectHeight+(mRectHeight/15)*(6-index)+mRectWidth*1.5);
int height_bootom = height_top-mRectHeight/10+(mRectHeight/15)*index;
height_top = (int) (height_top - mRectHeight/mRectY);//高度起始位置向0方向移動1/10屏幕
path.addRoundRect(new RectF(width, height_top, width+mRectWidth, height_bootom), radii,
Path.Direction.CCW);
canvas.drawPath(path, mPaint);
String RectX = String.valueOf(width+mRectWidth/2);
String RectY = String.valueOf(height_top);
mPointList.add(RectX+"-"+RectY);
Log.i("dg", "mPointList >>> "+ mPointList.size());
Path path1 = new Path();
path1.addRoundRect(new RectF(width, height_bootom+10, width+mRectWidth, height_bootom+12), radii,
Path.Direction.CCW);
canvas.drawPath(path1, mPaint);
switch (index) {
case 0:
mLevel = "L1-L3";
mName = "入門";
break;
case 1:
mLevel = "L4-L6";
mName = "初級";
break;
case 2:
mLevel = "L7-L9";
mName = "中級";
break;
case 3:
mLevel = "L10-L12";
mName = "中高級";
break;
case 4:
mLevel = "L13-L15";
mName = "高級";
break;
case 5:
mLevel = "L16";
mName = "專家";
break;
default:
break;
}
drawLevel(canvas, index, width, height_bootom,mLevel);
drawText(canvas, index, width, height_bootom,mName);
}
/**
* 畫名稱
* @param canvas
* @param index
* @param width
* @param height_bootom
* @param name
*/
private void drawText(Canvas canvas, int index, int width, int height_bootom, String name) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Style.FILL);
paint.setStrokeWidth(30f);
paint.setDither(true);
paint.setColor(getResources().getColor(colors[index]));
paint.setTextSize(30);
canvas.drawText(name , width+mRectWidth/5, height_bootom+100, paint);
}
/**
* 畫等級
* @param canvas
* @param index
* @param width
* @param height_bootom
* @param level
*/
private void drawLevel(Canvas canvas, int index, int width,
int height_bootom, String level) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Style.FILL);
paint.setStrokeWidth(30f);
paint.setDither(true);
paint.setColor(getResources().getColor(colors[index]));
paint.setTextSize(30);
if(index ==5){
canvas.drawText(level , width+mRectWidth/4, height_bootom+60, paint);
}else if(index == 4 || index ==3 ){
canvas.drawText(level , width+mRectWidth/20, height_bootom+60, paint);
}else{
canvas.drawText(level , width+mRectWidth/6, height_bootom+60, paint);
}
}
public ArrayList<String> getPointList() {
return mPointList;
}
public void setPointList(ArrayList<String> mPointList) {
this.mPointList = mPointList;
}
}
/**
* 主頁面
* @author DuGuang
* blog地址:http://blog.csdn.net/duguang77
*
*/
public class TestCourseReportActivity extends Activity {
private FrameLayout mFlMain;
private ArrayList<String> mPointList;
private CustomRect mCusRect;
private CustomTrigon mTrigon;
private TextView mTvHideOne, mTvLevel, mTvHideTwo,mTvHide;
private View mViewLine;
private int mWidth;
private int mHeight;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
//獲取5個柱狀體頂點中心座標位置
mPointList.addAll(mCusRect.getPointList());
String[] split = mPointList.get(5).split("-");
mTrigon.setData(new PointBean(Integer.parseInt(split[0]), Integer
.parseInt(split[1])));
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_course_testcourse_report);
initView();
initData();
}
private void initView() {
mFlMain = (FrameLayout) findViewById(R.id.fl_mian);
mCusRect = (CustomRect) findViewById(R.id.cus_rect);
mTvHideOne = (TextView) findViewById(R.id.tv_hide_one);
mTvLevel = (TextView) findViewById(R.id.tv_level);
mTvHideTwo = (TextView) findViewById(R.id.tv_hide_two);
mViewLine = findViewById(R.id.view_line);
mTvHide = (TextView) findViewById(R.id.tv_hide);
}
private void initData() {
mPointList = new ArrayList<String>();
CustomCircle circle = new CustomCircle(this);
mTrigon = new CustomTrigon(this);
mFlMain.addView(mTrigon,2);
mFlMain.addView(circle,3);
new Thread() {
public void run() {
//這裏啓動線程是爲了防止layout佈局文件還沒有完成,去獲取柱狀體頂部座標的時候Null異常
SystemClock.sleep(200);
mHandler.sendEmptyMessage(0);
};
}.start();
// 獲取屏幕寬高(方法1)
mWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕寬
mHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高
int width = mWidth / 2 - mWidth /8 ;
int height = mHeight / 4 - mHeight/12;
//這裏第一個TextView竟然顯示不出來,不知道爲什麼,做個標記,以後修改
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.topMargin = height+40;
params.leftMargin = width;
mTvHideOne.setLayoutParams(params);
FrameLayout.LayoutParams params4 = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params4.topMargin = height-10;
params4.leftMargin = width;
mTvHide.setLayoutParams(params4);
FrameLayout.LayoutParams params1 = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params1.topMargin = height+40;
params1.leftMargin = width;
mTvLevel.setTextColor(getResources().getColor(R.color.text_hide));
mTvLevel.setLayoutParams(params1);
FrameLayout.LayoutParams params2 = new FrameLayout.LayoutParams(
300, 1);
params2.topMargin = height+140;
params2.leftMargin = width;
mViewLine.setBackgroundColor(getResources().getColor(R.color.view_backgroud));
mViewLine.setLayoutParams(params2);
FrameLayout.LayoutParams params3 = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params3.topMargin = height+150;
params3.leftMargin = width;
mTvHideTwo.setTextColor(getResources().getColor(R.color.text_level));
mTvHideTwo.setLayoutParams(params3);
}
}
Demo下載地址:https://github.com/z56402344/Android_Graphics_Instance_One
項目這週週末會發到github上,大家等鏈接地址吧,如有什麼疑問請留言
轉載請註明:http://blog.csdn.net/duguang77/article/details/40869079