拿到美工效果圖,咱們程序員就得畫得一模一樣。 為了不被老板噴,只能多練啊。
聽說你覺得前面幾篇都so easy,那今天就帶你做個相對比較復雜的。
轉載請注明出處:http://blog.csdn.net/wingichoy/article/details/50468674
今天的效果圖如下(左邊是ui圖 右邊是實現圖):

自我感覺總體效果還不錯,至少大概畫得一樣了。上一個動態圖:

其實這個效果實現起來也不是很難,就是計算坐標,弧度之類的可能會比較麻煩,這里分享寫這個其中一張手稿,請無視掉很丑的字,其實做自定義view 還是要在紙上多畫。所以希望大家也能這么畫畫,思路會很順。

好的了,廢話不多說,快開始。
首先自定義屬性 ?構造函數,測量什么的 你肯定已經很熟練 直接貼代碼了,注釋寫的很清楚
~~~
public class PanelView extends View {
private int mWidth;
private int mHeight;
private int mPercent;
//刻度寬度
private float mTikeWidth;
//第二個弧的寬度
private int mScendArcWidth;
//最小圓的半徑
private int mMinCircleRadius;
//文字矩形的寬
private int mRectWidth;
//文字矩形的高
private int mRectHeight;
//文字內容
private String mText = "";
//文字的大小
private int mTextSize;
//設置文字顏色
private int mTextColor;
private int mArcColor;
//小圓和指針顏色
private int mMinCircleColor;
//刻度的個數
private int mTikeCount;
private Context mContext;
public PanelView(Context context) {
this(context, null);
}
public PanelView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.PanelView,defStyleAttr,0);
mArcColor = a.getColor(R.styleable.PanelView_arcColor, Color.parseColor("#5FB1ED"));
mMinCircleColor = a.getColor(R.styleable.PanelView_pointerColor,Color.parseColor("#C9DEEE"));
mTikeCount = a.getInt(R.styleable.PanelView_tikeCount,12);
mTextSize = a.getDimensionPixelSize(PxUtils.spToPx(R.styleable.PanelView_android_textSize,mContext),24);
mText = a.getString(R.styleable.PanelView_android_text);
mScendArcWidth = 50;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize;
}else {
mWidth = PxUtils.dpToPx(200,mContext);
}
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize;
}else {
mHeight = PxUtils.dpToPx(200,mContext);
}
Log.e("wing",mWidth+"");
setMeasuredDimension(mWidth, mHeight);
}
~~~
自定義屬性attr.xml
~~~
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PanelView">
<attr name="arcColor" format="color"/>
<attr name="arcWidth" format="dimension"/>
<attr name="android:text"/>
<attr name="tikeCount" format="integer"/>
<attr name="pointerColor" format="color"/>
<attr name="android:textSize"/>
</declare-styleable>
</resources>
~~~
之后來重頭戲,也就是繪制。就像畫畫一樣,再復雜的view也是一筆一筆畫出來的。所以我們把這個view分解。
大概分解成如下:**1.最外面的弧 ? 2.里面的粗弧 ? 3.中間小圓 ? 4.最小的圓 ?5.刻度 ? 6.指針 ?7.矩形 ?8.文字**
相信讓你分開畫一定難不倒你。那組合在一起 就是這個view啦。下面開始我們的ondraw()
按照這個分解來:
1.繪制最外面的弧 ? 這里需要注意的一點是,如果想讓這個圓在view里 記得減去畫筆寬度的一半 ?因為半徑是從圓心到畫筆寬度的中間算的,所以這里畫弧的矩形是 ?new RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth)
~~~
Paint p = new Paint();
int strokeWidth = 3;
p.setStrokeWidth(strokeWidth);
p.setAntiAlias(true);
p.setStyle(Paint.Style.STROKE);
p.setColor(mArcColor);
//最外面線條
canvas.drawArc(new RectF(strokeWidth, strokeWidth, mWidth - strokeWidth, mHeight - strokeWidth), 145, 250, false, p);
~~~
畫出來是這樣的效果。

2.繪制里面的粗弧,這里比較麻煩的就是需要分為四段,看圖:

因為大圓和里面粗弧的長短不一致,這里使用百分比來計算 所以會造成指針偏差,那么這里把 1、2兩個部分固定來畫,然后是3 充滿的部分,用百分比來計算需要畫多少度,最后是4 空白的部分。
首先把粗弧的矩形畫出來,這里固定了比大弧半徑少50(這里其實可以改進,你可以改成動態的讓他更靈活),然后計算出百分比。
~~~
RectF secondRectF = new RectF(strokeWidth + 50, strokeWidth + 50, mWidth - strokeWidth - 50, mHeight - strokeWidth - 50);
float secondRectWidth = mWidth - strokeWidth - 50 - (strokeWidth + 50);
float secondRectHeight = mHeight - strokeWidth - 50 - (strokeWidth + 50);
float percent = mPercent / 100f;
~~~
接下來繪制1弧,先算出fill充滿部分的度數,因為是突出的,所以如果百分比為0,突出左端為白色 如果不為零,則和充滿顏色統一。
~~~
//充滿的圓弧的度數 -5是大小弧的偏差
float fill = 250 * percent ;
//空的圓弧的度數
float empty = 250 - fill;
// Log.e("wing", fill + "");
if(percent==0){
p.setColor(Color.WHITE);
}
//畫粗弧突出部分左端
canvas.drawArc(secondRectF,135,11,false,p);
~~~
然后繪制2弧 也就是fill充滿的弧,
~~~
canvas.drawArc(secondRectF, 145, fill, false, p);
~~~
接下來是3弧,也就是empty未充滿的弧,是白色的
~~~
p.setColor(Color.WHITE);
//畫弧胡的未充滿部分
canvas.drawArc(secondRectF, 145 + fill, empty, false, p);
~~~
最后,畫出右邊突出的4弧, 如果百分比為100 那么和充滿的顏色一致,否則為白色
~~~
//畫粗弧突出部分右端
if(percent == 1){
p.setColor(mArcColor);
}
canvas.drawArc(secondRectF,144+fill+empty,10,false,p);
~~~
這樣粗弧也就畫完了 來看看效果,就畫了兩條弧線(實際是5條),就成型了。

3.中間的小圓外圈,他的圓心不用多說 是整個view的中心
~~~
p.setColor(mArcColor);
//繪制小圓外圈
p.setStrokeWidth(3);
canvas.drawCircle(mWidth / 2, mHeight / 2, 30, p);
~~~
4.繪制內圓,圓心一樣的,半徑和畫筆粗度改變一下
~~~
//繪制小圓內圈
p.setColor(mMinCircleColor);
p.setStrokeWidth(8);
mMinCircleRadius = 15;
canvas.drawCircle(mWidth / 2, mHeight / 2, mMinCircleRadius, p);
~~~

5.刻度 ?刻度處理起來可能比較麻煩,用三角函數算坐標啊 循環畫出來。。 這里提供一種比較簡單的方法:旋轉畫布。
首先引入一個概念,什么叫旋轉畫布呢,就是把你的畫布旋轉。。經過測試,旋轉以后,整個坐標軸都會對應旋轉,一張圖舉例說明下。

大概就是這個意思,畫布旋轉之后 坐標系也就旋轉了,但是原來的圖像還在,所以說你比如這個點 x,y旋轉前在這個位置, 那么旋轉后就是另外一個位置了,但是他們的坐標是相同的。 所以刻度也可以考這種方法畫。我們只要畫出最頂端的刻度 然后旋轉就可以了。

繪制第一段刻度, 然后總共是250的弧度 ?計算出每個刻度的度數 ? ? 用250除以刻度數mTikeCount,就是每次旋轉的度數。接下來把畫布逐步旋轉,按照原坐標繪制,即可繪制出右半部分刻度。 ?注意:為了讓之后的繪制正常,務必把畫布轉回原來的位置
~~~
//繪制刻度!
p.setColor(mArcColor);
//繪制第一條最上面的刻度
mTikeWidth = 20;
p.setStrokeWidth(3);
canvas.drawLine(mWidth / 2, 0, mWidth / 2, mTikeWidth, p);
//旋轉的角度
float rAngle = 250f / mTikeCount;
//通過旋轉畫布 繪制右面的刻度
for (int i = 0; i < mTikeCount / 2; i++) {
canvas.rotate(rAngle, mWidth / 2, mHeight / 2);
canvas.drawLine(mWidth / 2, 0, mWidth / 2, mTikeWidth, p);
}
//現在需要將將畫布旋轉回來
canvas.rotate(-rAngle * mTikeCount / 2, mWidth / 2, mHeight / 2);
~~~

左半部分同理,需要改變的度數為負 就好了
~~~
//通過旋轉畫布 繪制左面的刻度
for (int i = 0; i < mTikeCount / 2; i++) {
canvas.rotate(-rAngle, mWidth / 2, mHeight / 2);
canvas.drawLine(mWidth / 2, 0, mWidth / 2, mTikeWidth, p);
}
//現在需要將將畫布旋轉回來
canvas.rotate(rAngle * mTikeCount / 2, mWidth / 2, mHeight / 2);
~~~

6.指針 ? 指針的繪制和刻度相似,先算出來百分比所占的度數 然后根據 是否大于50%來旋轉畫布。
指針的起終點是 總view高度的一半 粗弧矩形的一半 加上小圓,前面坐標講解了那么,這個也一樣,自己拿起筆算一算。
注意這里畫布旋轉我通過計算得出一個公式 250 * percent - 250/2。
如果小于50% 則為負 ? 如果大于50%則為正,然后進行旋轉。
切忌最后一定要將畫布轉回來。
~~~
//繪制指針
p.setColor(mMinCircleColor);
p.setStrokeWidth(4);
//按照百分比繪制刻度
canvas.rotate(( 250 * percent - 250/2), mWidth / 2, mHeight / 2);
canvas.drawLine(mWidth / 2, (mHeight / 2 - secondRectHeight / 2) + mScendArcWidth / 2 + 2, mWidth / 2, mHeight / 2 - mMinCircleRadius, p);
//將畫布旋轉回來
canvas.rotate(-( 250 * percent - 250/2), mWidth / 2, mHeight / 2);
~~~

接下來就是畫矩形和文字。沒什么好說的了,坐標也是X周圍mWidth/2 ? y軸自己根據圓心微調一個距離
~~~
//繪制矩形
p.setStyle(Paint.Style.FILL);
p.setColor(mArcColor);
mRectWidth = 60;
mRectHeight = 25;
//文字矩形的最底部坐標
float rectBottomY = mHeight/2 + secondRectHeight/3+mRectHeight;
canvas.drawRect(mWidth/2-mRectWidth/2,mHeight/2 + secondRectHeight/3,mWidth/2+mRectWidth/2,rectBottomY,p);
p.setTextSize(mTextSize);
mTextColor = Color.WHITE;
p.setColor(mTextColor);
float txtLength = p.measureText(mText);
canvas.drawText(mText,(mWidth-txtLength)/2,rectBottomY + 40,p);
super.onDraw(canvas);
~~~
這樣完成了整個view的繪制。

下面要做的就是為了方便使用者,提供一些設置屬性的方法。
~~~
/**
* 設置百分比
* @param percent
*/
public void setPercent(int percent) {
mPercent = percent;
invalidate();
}
/**
* 設置文字
* @param text
*/
public void setText(String text){
mText = text;
invalidate();
}
/**
* 設置圓弧顏色
* @param color
*/
public void setArcColor(int color){
mArcColor = color;
invalidate();
}
/**
* 設置指針顏色
* @param color
*/
public void setPointerColor(int color){
mMinCircleColor = color;
invalidate();
}
/**
* 設置文字大小
* @param size
*/
public void setTextSize(int size){
mTextSize = size;
invalidate();
}
/**
* 設置粗弧的寬度
* @param width
*/
public void setArcWidth(int width){
mScendArcWidth = width;
invalidate();
}
~~~
大功告成!!!一個看似復雜的view ?經過我們一步一步繪制遍完成了。
其實技術的養成也是這樣,只要一步一步腳踏實地的去練習,我相信總有一天我能成為大神。
本項目地址 :[PanelView](https://github.com/githubwing/PanelView)? ?求關注 ?求評論 ?求star!!!!!!
- 前言
- android自定義viewgroup初步之一----抽屜菜單
- Android 自定義view --圓形百分比(進度條)
- Android 自定義View -- 簡約的折線圖
- 新手自定義view練習實例之(一) 泡泡彈窗
- 新手自定義view練習實例之(二) 波浪view
- 手把手帶你畫一個 時尚儀表盤 Android 自定義View
- 手把手帶你畫一個動態錯誤提示 Android自定義view
- 手把手帶你做一個超炫酷loading成功動畫view Android自定義view
- 關于Android自定義view 你所需要知道的基本函數
- Android自定義view進階-- 神奇的貝塞爾曲線
- wing帶你玩轉自定義view系列(1) 仿360內存清理效果
- wing帶你玩轉自定義view系列(2) 簡單模仿qq未讀消息去除效果
- wing帶你玩轉自定義view系列(3)模仿微信下拉眼睛
- 手把手教你畫一個 逼格滿滿圓形水波紋loadingview Android
- 有坑?? 為何wing墜入PorterDuffXferMode的萬丈深淵(PorterDuffXferMode深入試驗)
- 手把手帶你畫一個漂亮蜂窩view Android自定義view
- 一個炫字都不夠??!!!手把手帶你打造3D自定義view
- 恭喜發財! -- 手把手教你仿造一個qq下拉搶紅包 Android自定義view