Android绘图是开发中比较常用的功能,虽然我用的少(层次不够^O^ ),对绘图的基本用法,适当记录一下。
Android绘图用到的两个重要的类是:
- Paint.class(画笔)
- Canvas.class (画布)
View绘图分三个重要步骤:
- Measure 测量
- Layout 布局
- Draw 绘制
知道了这些,就开始我们的demo吧。
Demo介绍:血糖含量在不同日期的变化折线图。
先上效果图:
XML布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_white">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
card:cardBackgroundColor="@color/color_blue"
card:cardCornerRadius="5dp"
card:cardElevation="1dp"
card:cardMaxElevation="1dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_health_data_bloodsugar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:text="血糖 6.5mmol/L"
android:textColor="@android:color/white"
android:textSize="16sp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="10dp">
<Button
android:id="@+id/btn_health_item_day"
style="@style/item_health_data_buttonStyle"
android:text="日"/>
<Button
android:id="@+id/btn_health_item_week"
style="@style/item_health_data_buttonStyle"
android:text="周"/>
<Button
android:id="@+id/btn_health_item_month"
style="@style/item_health_data_buttonStyle"
android:text="月"/>
<Button
android:id="@+id/btn_health_item_year"
style="@style/item_health_data_buttonStyle"
android:text="年"
/>
</LinearLayout>
<com.doctor.comonent.MyHealthDataView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
自定义绘制View
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import com.doctor.util.LogUtil;
import com.doctor.util.NumberUtil;
/**
* Created by hgb
* Date 2016/8/30 17:01.
* Description : 自定义健康数据的view,用于用户健康数据的展示
*/
public class MyHealthDataView extends View {
private Paint paint; //画笔
private int width; //视图宽度
private int height; //视图的高度
//坐标原点位置
private int originX = 50;
private int originY = 400;
//画笔颜色
private int paint_color_grey = Color.rgb(118, 188, 240);
//表示最下面的日期.
private String[] arrStr = new String[]{"19", "20", "21", "22", "23", "24", "25"};
//对应日期不同的血糖值.空腹血糖正常范围是 3.9-6.1mmol/L(70-110mg/dL),正常餐后两小时血糖范围是 3.9-7.8mmol/L(70-140mg/dL)
private Float[] arrFloat = new Float[]{1.9f, 5.4f, 6.1f, 4.0f, 5.5f, 2.5f, 4.8f};
private float fSpace; //水平方向x轴的间距.
private int size; //代表需要绘制的数据量
private Float[] arrScale; //点的高度占整体高度的比例
public MyHealthDataView(Context context) {
super(context);
initPaint();
}
public MyHealthDataView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public MyHealthDataView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
/**
* 开始进行绘制
*/
public void setData() {
postInvalidate();
}
/**
* 初始化view
*/
private void initPaint() {
//创建画笔.
paint = new Paint();
paint.setColor(paint_color_grey);
//设置画笔宽度
paint.setStrokeWidth(3);
//设置画笔抗锯齿
paint.setAntiAlias(true);
}
/**
* 测量
*
* @param widthMeasureSpec 宽
* @param heightMeasureSpec 高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
LogUtil.e("width", width + "");
height = MeasureSpec.getSize(heightMeasureSpec) - 60;
LogUtil.e("height", height + "");
// originX = 15;
originY = height;
}
/**
* 在ViewGroup位置中所处的位置
*
* @param changed 当前View的大小和位置改变了
* @param left 左部位置(相对于父视图)
* @param top 顶部位置(相对于父视图)
* @param right 右部位置(相对于父视图)
* @param bottom 底部位置(相对于父视图)
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
/**
* 绘制
*
* @param canvas 画布
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制Y轴
drawAxisY(canvas, paint);
//绘制X轴坐标线(及下面的数据)
drawAxisScaleMarkX(canvas, paint);
//绘制水平的浅线
drawHorizontalLine(canvas, paint);
//绘制数据点,根据点连线
drawPoint(canvas, paint);
//根据点连线
drawLine(canvas, paint);
}
/**
* 绘制横坐标轴(X轴)
*/
private void drawAxisX(Canvas canvas, Paint paint) {
//画竖轴(Y)
canvas.drawLine(originX, originY, originX, originY - height, paint);//参数说明:起始点左边x,y,终点坐标x,y,画笔
}
/**
* 绘制纵坐标轴(Y轴)
*/
private void drawAxisY(Canvas canvas, Paint paint) {
//画横轴(X)
canvas.drawLine(originX, originY, width - originX, originY, paint);
}
/**
* 绘制X轴刻度线及下面的数据
*
* @param canvas
* @param paint
*/
private void drawAxisScaleMarkX(Canvas canvas, Paint paint) {
paint.setTextSize(40);
size = arrStr.length; //文本数量
int axisX = originX - 5; //x轴距离,-5是为了让坐标点和数据对齐
int axisY = originY + 40; //Y轴坐标刻度值
fSpace = ((float) width - 2 * originX) / (size - 1) - 3.0f;//Y轴数据之间的间距
for (int i = 0; i < size; i++) {
canvas.drawText(arrStr[i], axisX + fSpace * i, axisY, paint);
}
}
/**
* 绘制数据点
*
* @param canvas
* @param paint
*/
private void drawPoint(Canvas canvas, Paint paint) {
//重写选取给paint赋值属性
paint.setColor(Color.WHITE); //设置画笔颜色
paint.setStyle(Paint.Style.STROKE); //设置画笔模式为描边
paint.setStrokeWidth(10f); //设置画笔宽度为10px
arrScale = NumberUtil.getArrScale(arrFloat); //获取数组中每个值与最大值的比
//绘制一组点,坐标位置由float数组指定
for (int i = 0; i < size; i++) {
canvas.drawPoint(originX + fSpace * i, (height - height * arrScale[i] + 10), paint);
}
}
/**
* 绘制水平横线
*
* @param canvas
* @param paint
*/
private void drawHorizontalLine(Canvas canvas, Paint paint) {
int unit = -70;
canvas.drawLines(new float[]{
originX, originY + unit, width - originX, originY + unit,
originX, originY + unit * 2, width - originX, originY + unit * 2,
originX, originY + unit * 3, width - originX, originY + unit * 3,
originX, originY + unit * 4, width - originX, originY + unit * 4,
originX, originY + unit * 5, width - originX, originY + unit * 5
}, paint);
}
/**
* 根据点来连线
*
* @param canvas
* @param paint
*/
private void drawLine(Canvas canvas, Paint paint) {
paint.setStrokeWidth(5f); //设置画笔宽度为10px
Path path = new Path(); // 创建Path
path.moveTo(originX, height - height * arrScale[0] + 10); // moveTo 把动作的起点移动到第一个点
for (int i = 1; i < size; i++) {
path.lineTo(originX + fSpace * i, height - height * arrScale[i] + 10); // lineTo 把剩下的点连接起来.
}
canvas.drawPath(path, paint); // 绘制Path
}
}
代码中用到的一个工具类NumberUtil
/**
* Created by hgb
* Date 2016/8/31 15:16.
* Description : 处理数值的一些逻辑
*/
public class NumberUtil {
/**
* 获取一个float数组中的最大值
*
* @param arrFloat float类型数组
* @return 返回数值中的最大值.
*/
public static float getMaxNum(Float[] arrFloat) {
int i;
float max;
int size = arrFloat.length;
max = arrFloat[0];
for (i = 0; i < size; i++) {
if (arrFloat[i] > max) // 判断最大值
max = arrFloat[i];
}
return max;
}
/**
* 获取数组中每个值与最大值的比
*
* @param arrFloat float类型数组
* @return 比例
*/
public static Float[] getArrScale(Float[] arrFloat) {
float maxNum = getMaxNum(arrFloat);
int i;
int size = arrFloat.length;
Float[] arrScale = new Float[size];
for (i = 0; i < size; i++) {
arrScale[i] = arrFloat[i] / maxNum;
}
return arrScale;
}
}
这样,一个图表基本上就完成了。
Android中有很多复杂的绘图,paint和canvas的方法也多种多样,具体参见api详细文档。