**摘要**
本文主要内容是:在Android系统下自定义图形。效果如下图:
**思路**
如上图所示,我们应该把问题简单化,看上去图片是有规律的,类似一个列表,那么我们就当一列是一个Item吧。
再把一个Item分解: 分成文字和图形部分。
文字部分
文字部分就是对当个柱状图的说明,我们暂且叫他“itemName”吧。我们看到了itemName是右对齐的,那么我们就得获取所有数据的itemName,然后看谁最长,然后计算这段文字有多长,此时,我们就知道了itemName所占的长度了(当个文字的长度 * 文字个数),获取当个文字的长宽方法如下:
/**
* 获取单个字符的高和宽
*/
private int[] getTextWH() {
int[] wh = new int[2];
// 一个矩形
Rect rect = new Rect();
String text = "我";
Paint paint = new Paint();
// 设置文字大小
paint.setTextSize(dip2px(getContext(), 16));
paint.getTextBounds(text, 0, text.length(), rect);
wh[0] = rect.width();
wh[1] = rect.height();
return wh;
}
那么我们就得设置文字画笔是右对齐的。
/**
*绘制文字说明 右对齐
* @param canvas
* @param text
*/
private void drawText(Canvas canvas, String text) {
int x = getWidth();
int y = getHeight();
Paint textPaint = new Paint();
textPaint.setColor(getResources().getColor(R.color.text_line_chart));
textPaint.setTextSize(dip2px(getContext(), 16));
// 设置文字右对齐
textPaint.setTextAlign(Paint.Align.RIGHT);
float tX = (x - getFontlength(textPaint, text)) / 2;
float tY = (y - getFontHeight(textPaint)) / 2 + getFontLeading(textPaint);
// 注意第二个参数,右对齐,文字是从右开始写的,那么 x 就是对齐处的X坐标
canvas.drawText(text, mTextW, tY, textPaint);
}
图形部分
图形部分主要是绘制矩形,那么怎么绘制呢?很简单的嘛: 先获取屏幕宽度SW,然后获取数值的大小DV, SW / DV 就得到了一个单位DV所占的位置了,然后直接canvas.drawRect …..但是,但是 我们是画一个列表的,要对其的,我们要的是数据有参考、对比性的。是吧???那么我们就得算出所有 DV的最大值。然后计算一个DV所占的位置,最后才能绘制。 记得,我们的图形是在 ItemName右边的。那么我们图形的X坐标就是 ItemName所占的宽度开始的。
/**绘制图形
* @param canvas
*/
private void drawLine(Canvas canvas) {
double chart_length = (getWidth() - mTextW) / (double) mMaxV;
int start_complete_left = mTextW + 10,
start_complete_top = 4,
start_complete_right = start_complete_left + (int) (chart_length * mData.getRecover_complete())- dip2px(getContext(), 6),
start_uncomplete_right = start_complete_left + (int) (chart_length * (mData.getRecover_complete() + mData.getRecover_uncomplete()))- dip2px(getContext(), 6);
Log.e("TAG", start_complete_left + "..." + start_complete_top + ",,," + start_complete_right + "ds"
+ start_uncomplete_right);
this.arcPaint = new Paint();
this.arcPaint.setColor(getResources().getColor(R.color.line_chart_uncomplete));
this.arcPaint.setAntiAlias(true);// 去除锯齿
// 绘制未完成的,
canvas.drawRect(start_complete_left, start_complete_top, start_uncomplete_right, mChartH, arcPaint);
// 绘制完成的
this.arcPaint.setColor(getResources().getColor(R.color.line_chart_complete));
canvas.drawRect(start_complete_left, start_complete_top, start_complete_right, mChartH, arcPaint);
}
文字加图形
画好一个,那么我们就得把 一个个item 放进列表里面去, 那么就得重写一个LinearLayout, 然后add()上去。 为什么不用ListView或者RecycleView?你一定在想,因为itemName的长度不确定,我们又得右对齐,所以只能用LinearLayout了
public LinChartLayout(Context context) {
super(context);
this.setOrientation(VERTICAL);
setView();
}
public void setView() {
if (mData != null && !mData.isEmpty()) {
int text_max_length = 0;
int value_max = 0;
for (LineChartData data : mData) {
// 获取最长文字的个数
if (text_max_length <= data.getName().length()) {
text_max_length = data.getName().length();
}
// 获取数据值的大小
int total = data.getRecover_complete() + data.getRecover_uncomplete();
// 获取数据的值
if (value_max <= total) {
value_max = total;
}
}
int[] wh = getTextWH();
// 文字区域的宽
int textAreW = text_max_length * wh[0] + dip2px(getContext(), 10);
// 图形区域的宽
int chartAreW = scrW - textAreW - 10;
LinearLayout.LayoutParams layoutParams = new LayoutParams(scrW - dip2px(getContext(), 10),dip2px(getContext(), 32));
// 设置居中
layoutParams.gravity = Gravity.CENTER;
// 设置Margin
layoutParams.topMargin = dip2px(getContext(), 4);
layoutParams.bottomMargin = dip2px(getContext(), 4);
// 遍历添加LinChartView
for (LineChartData i : mData) {
LinChartView chartView = new LinChartView(getContext());
chartView.setData(textAreW, chartAreW,value_max, i);
this.addView(chartView, layoutParams);
}
}
}
**全部代码**
下面是一些参考的代码:
LinChartLayout.java
/**
* @author bishiqiangan@yeah.net
* 重写一个LinearLayout, 遍历添加LinChartView
*/
public class LinChartLayout extends LinearLayout {
/**
* 列表的数据源
*/
private List<LineChartData> mData;
/**
* 屏幕的宽
*
*/
private int scrW;
public LinChartLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public LinChartLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LinChartLayout(Context context) {
super(context);
this.setOrientation(VERTICAL);
setView();
}
public void setView() {
if (mData != null && !mData.isEmpty()) {
int text_max_length = 0;
int value_max = 0;
for (LineChartData data : mData) {
// 获取最长文字的个数
if (text_max_length <= data.getName().length()) {
text_max_length = data.getName().length();
}
// 获取数据值的大小
int total = data.getRecover_complete() + data.getRecover_uncomplete();
// 获取数据的值
if (value_max <= total) {
value_max = total;
}
}
int[] wh = getTextWH();
// 文字区域的宽
int textAreW = text_max_length * wh[0] + dip2px(getContext(), 10);
// 图形区域的宽
int chartAreW = scrW - textAreW - 10;
LinearLayout.LayoutParams layoutParams = new LayoutParams(scrW - dip2px(getContext(), 10),dip2px(getContext(), 32));
// 设置居中
layoutParams.gravity = Gravity.CENTER;
// 设置Margin
layoutParams.topMargin = dip2px(getContext(), 4);
layoutParams.bottomMargin = dip2px(getContext(), 4);
// 遍历添加LinChartView
for (LineChartData i : mData) {
LinChartView chartView = new LinChartView(getContext());
chartView.setData(textAreW, chartAreW,value_max, i);
this.addView(chartView, layoutParams);
}
}
}
/**
* 获取单个字符的高和宽
*/
private int[] getTextWH() {
int[] wh = new int[2];
// 一个矩形
Rect rect = new Rect();
String text = "我";
Paint paint = new Paint();
// 设置文字大小
paint.setTextSize(dip2px(getContext(), 16));
paint.getTextBounds(text, 0, text.length(), rect);
wh[0] = rect.width();
wh[1] = rect.height();
return wh;
}
public void setData(List<LineChartData> d,int scrw) {
this.mData = d;
this.scrW = scrw;
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
LinChartView.java
/**
* @author bishiqiangan@yeah.net
* 重写一个View , 绘制一个横向柱状图
*/
public class LinChartView extends View {
private LineChartData mData;
private int mTextW, mChartH, mMaxV;
private Paint arcPaint = null;
public LinChartView(Context context) {
super(context);
}
public LinChartView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LinChartView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData == null) {
return;
}
// 画文字
drawText(canvas, mData.getName());
//画图形
drawLine(canvas);
}
/**绘制图形
* @param canvas
*/
private void drawLine(Canvas canvas) {
double chart_length = (getWidth() - mTextW) / (double) mMaxV;
int start_complete_left = mTextW + 10,
start_complete_top = 4,
start_complete_right = start_complete_left + (int) (chart_length * mData.getRecover_complete())- dip2px(getContext(), 6),
start_uncomplete_right = start_complete_left + (int) (chart_length * (mData.getRecover_complete() + mData.getRecover_uncomplete()))- dip2px(getContext(), 6);
Log.e("TAG", start_complete_left + "..." + start_complete_top + ",,," + start_complete_right + "ds"
+ start_uncomplete_right);
this.arcPaint = new Paint();
this.arcPaint.setColor(getResources().getColor(R.color.line_chart_uncomplete));
this.arcPaint.setAntiAlias(true);// 去除锯齿
// 绘制未完成的,
canvas.drawRect(start_complete_left, start_complete_top, start_uncomplete_right, mChartH, arcPaint);
// 绘制完成的
this.arcPaint.setColor(getResources().getColor(R.color.line_chart_complete));
canvas.drawRect(start_complete_left, start_complete_top, start_complete_right, mChartH, arcPaint);
}
/**
*绘制文字说明 右对齐
* @param canvas
* @param text
*/
private void drawText(Canvas canvas, String text) {
int x = getWidth();
int y = getHeight();
Paint textPaint = new Paint();
textPaint.setColor(getResources().getColor(R.color.text_line_chart));
textPaint.setTextSize(dip2px(getContext(), 16));
// 设置文字右对齐
textPaint.setTextAlign(Paint.Align.RIGHT);
float tX = (x - getFontlength(textPaint, text)) / 2;
float tY = (y - getFontHeight(textPaint)) / 2 + getFontLeading(textPaint);
// 注意第二个参数,右对齐,文字是从右开始写的,那么 x 就是对齐处的X坐标
canvas.drawText(text, mTextW, tY, textPaint);
}
/**
* @return 返回指定笔和指定字符串的长度
*/
public static float getFontlength(Paint paint, String str) {
return paint.measureText(str);
}
/**
* @return 返回指定笔的文字高度
*/
public static float getFontHeight(Paint paint) {
Paint.FontMetrics fm = paint.getFontMetrics();
return fm.descent - fm.ascent;
}
/**
* @return 返回指定笔离文字顶部的基准距离
*/
public static float getFontLeading(Paint paint) {
Paint.FontMetrics fm = paint.getFontMetrics();
return fm.leading - fm.ascent;
}
public void setData(int textW, int chartW, int max_valur, LineChartData data) {
Log.e("TAG", max_valur + "...max");
this.mTextW = textW;
this.mChartH = chartW;
this.mMaxV = max_valur;
this.mData = data;
this.postInvalidate();
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
LineChartData.java
/**
* @author bishiqiangan@yeah.net
* 数据模型类
*/
public class LineChartData {
private int recover_complete;
private String name;
private int recover_uncomplete;
public int getRecover_complete() {
return recover_complete;
}
public void setRecover_complete(int recover_complete) {
this.recover_complete = recover_complete;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRecover_uncomplete() {
return recover_uncomplete;
}
public void setRecover_uncomplete(int recover_uncomplete) {
this.recover_uncomplete = recover_uncomplete;
}
}
调用的方法:
==android:orientation=”vertical”== 记得添加
<com.example.testandroid.LinChartLayout
android:id="@+id/linechart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="6dip"
==android:orientation="vertical"==
/>