最近,專案中遇到一個地方,要用到柱狀圖。所以這篇文章主要講怎麼搞一個柱子。 100行程式碼,搞定柱狀圖!
我的印象中柱子是這樣的。
恩,簡單,一個View直接放到xml,搞定! 但,設計師給的柱子是這樣的:
圓角,頭頂帶數字。恩,這樣用drawable也可以搞定。 但是,這個柱子是有一個動畫的,就是進入到介面的時候柱子不斷的長高。 這樣的話,綜合考慮還是用自定義View來做比較簡便。下面講一下思路。首先忽略動畫,先把靜態的效果做出來。
關於尺寸
控制元件尺寸直接來自xml中的設定,無需進行onMeasure測量。所以使用getWidth和getHeight獲取高度。
關於資料範圍
資料如果是一個柱子單獨顯示,則資料的範圍不是很重要,但是柱狀圖通常是由很多柱子並列顯示的,而這些柱子的單位高度都應該是一樣的,所以提供設定最大值的範圍,最小值就是0.
關於數字的文字大小
由於柱子的寬度就是整個View的寬度,所以數字的寬度不能超過柱子的寬度。因為這個原因,文字的size需要動態計算。意思就是 0和100000這兩個數字顯示的時候,文字的大小是不一樣的。
關於邊界值
0,是一個邊界值(最小值),當顯示0的時候,並不是柱子不顯示的,而是顯示一個最小高度的。
關於動畫
不停的設定值,就會形成動畫。意思是先設定資料1,然後緊接著設資料2.3.4.5……一直到最終的顯示值,就會有動畫效果。但是如果最終數值很大,1,1,1的增加就會很慢,動畫時間很長。
程式碼如下:
public class PPColumn extends View {
int MAX = 100;//最大
int corner = 40;
int data = 0;//顯示的數
int tempData = 0;
int textPadding = 20;
Paint mPaint;
int mColor;
Context mContext;
public PPColumn(Context context) {
super(context);
mContext = context;
}
public PPColumn(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mContext = context;
initPaint();
}
public PPColumn(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mColor = mContext.getResources().getColor(R.color.colorPrimary);
mPaint.setColor(mColor);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if(data == 0){
mPaint.setTextSize(getWidth() / 2);
RectF oval3 = new RectF(0, getHeight() - DPUnitUtil.px2dip(mContext,20), getWidth(), getHeight());// 設定個新的長方形
canvas.drawRoundRect(oval3, DPUnitUtil.px2dip(mContext,corner), DPUnitUtil.px2dip(mContext,corner), mPaint);
canvas.drawText("0",
getWidth() * 0.5f - mPaint.measureText("0") * 0.5f ,
getHeight() - DPUnitUtil.px2dip(mContext,20) - 2 * DPUnitUtil.px2dip(mContext,textPadding),
mPaint);
return;
}
//防止數值很大的的時候,動畫時間過長
int step = data /100 + 1;
if(tempData < data - step){
tempData = tempData + step;
}else{
tempData = data;
}
//畫圓角矩形
String S = tempData + "";
//一個字和兩,三個字的字號相同
if(S.length()<4){
mPaint.setTextSize(getWidth() / 2);
}else{
mPaint.setTextSize(getWidth() / (S.length()-1));
}
float textH = mPaint.ascent() + mPaint.descent();
float MaxH = getHeight() - textH - 2 * DPUnitUtil.px2dip(mContext,textPadding);
//圓角矩形的實際高度
float realH = MaxH / MAX * tempData;
RectF oval3 = new RectF(0, getHeight() - realH, getWidth(), getHeight());// 設定個新的長方形
canvas.drawRoundRect(oval3, DPUnitUtil.px2dip(mContext,corner), DPUnitUtil.px2dip(mContext,corner), mPaint);
//寫數字
canvas.drawText(S,
getWidth() * 0.5f - mPaint.measureText(S) * 0.5f ,
getHeight() - realH - 2 * DPUnitUtil.px2dip(mContext,textPadding),
mPaint);
if(tempData != data){
postInvalidate();
}
}
public void setData(int data, int MAX){
this.data = data;
tempData = 0;
this.MAX = MAX;
postInvalidate();
}
}
複製程式碼
我是在xml中這樣使用的,也就是上面效果的樣子。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.2" />
<com.allrun.arsmartelevatorformanager.widget.PPColumn
android:id="@+id/weihu_column"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2.4" />
<com.allrun.arsmartelevatorformanager.widget.PPColumn
android:id="@+id/weixiu_column"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2.4" />
<com.allrun.arsmartelevatorformanager.widget.PPColumn
android:id="@+id/jiuyuan_column"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.2" />
</LinearLayout>
複製程式碼
使用,設定值。Max最後要加1,防止0.幾的時候計算錯誤。
int max = (int) (Math.max(Math.max(weihu, weixiu), jiuyuan) * 1.2) + 1;
weihuColumn.setData(weihu, max);
複製程式碼
最後效果圖