在Android开发中,自定义View是一个比较常见的需求。本文将详细介绍如何创建一个垂直轴线视图VerticalAxisView,以及如何进行相关属性设置和绘制。
下面是效果图
一、需求分析
本次需求是创建一个垂直轴线视图,包括以下功能:
- 可以设置轴线的颜色;
- 可以设置刻度数量;
- 可以设置最大值和最小值;
- 可以设置标签字体大小;
- 可以选择标签在轴线左侧还是右侧,并针对不同方向进行相应绘制。
二、实现步骤
- 创建VerticalAxisView类并继承自View;
- 定义一些默认属性,如:默认刻度数量、默认最小值、默认最大值、默认颜色值、默认文本颜色、默认文本大小等;
- 在构造方法中初始化画笔,并调用init()方法进行初始化;
- 实现onDraw()方法,在该方法中进行绘制轴线、刻度和标签等;
- 提供外部可调用的方法,如:setTickCount()、setMinValue()、setMaxValue()、setAxisColor()和setTextSize()等;
- 最后,在XML布局文件中进行引用。
下面是具体实现代码:
public class VerticalAxisView extends View {
private static final int DEFAULT_TICK_COUNT = 6;
private static final float DEFAULT_MIN_VALUE = 0f;
private static final float DEFAULT_MAX_VALUE = 100f;
private static final String DEFAULT_COLOR_VALUE = "#000000";
private static final int DEFAULT_TEXT_SIZE = 12;
//0:文本在轴线左侧,1:文本在轴线右侧,没有用枚举了
private static final int DEFAULT_DIRECTION = 0;
private Paint mAxisPaint = new Paint();
private Paint mTickPaint = new Paint();
private Paint mTextPaint = new Paint();
private int mTickCount = DEFAULT_TICK_COUNT;
private float mMinValue = DEFAULT_MIN_VALUE;
private float mMaxValue = DEFAULT_MAX_VALUE;
private float mValueRange = mMaxValue - mMinValue;
private float mTickSpacing = mValueRange / (mTickCount - 1);
//文本在轴线左侧还是右侧
private int drawDirection = DEFAULT_DIRECTION;
public VerticalAxisView(Context context) {
super(context);
init();
}
public VerticalAxisView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public VerticalAxisView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// 初始化画轴线画笔
mAxisPaint.setColor(Color.parseColor(DEFAULT_COLOR_VALUE));
mAxisPaint.setStrokeWidth(1);
mAxisPaint.setAntiAlias(true);
// 初始化刻度线画笔
mTickPaint.setColor(Color.parseColor(DEFAULT_COLOR_VALUE));
mTickPaint.setStrokeWidth(1);
mTickPaint.setAntiAlias(true);
// 初始化刻度文本画笔
mTextPaint.setColor(Color.parseColor(DEFAULT_COLOR_VALUE));
mTextPaint.setTextSize(DEFAULT_TEXT_SIZE);
mTextPaint.setAntiAlias(true);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
float tickLength = width / 9f;
float textPadding = 8;
float startY = height - (mMaxValue - mMinValue) / mValueRange * (height - 2 * textPadding - 40) - textPadding - 20;
float endY = height - textPadding - 20;
if (drawDirection == 0) {
// 绘制轴线
canvas.drawLine(width - tickLength, startY, width - tickLength, endY, mAxisPaint);
// 绘制刻度和标签
for (int i = 0; i < mTickCount; i++) {
float tickValue = mMinValue + i * mTickSpacing;
float tickPosition = height - ((tickValue - mMinValue) / mValueRange) * (height - 2 * textPadding - 40) - textPadding - 20;
canvas.drawLine(width - tickLength, tickPosition, width, tickPosition, mTickPaint);
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float baseline = tickPosition - (fontMetrics.top + fontMetrics.bottom) / 2;
// 数值要格式化一位小数
String tickValueStr = String.format("%.1f", tickValue);
canvas.drawText(tickValueStr, width - 2 * tickLength, baseline, mTextPaint);
}
}else {
// 绘制轴线,起始位置为0+刻度线的宽度
canvas.drawLine(tickLength, startY, tickLength, endY, mAxisPaint);
// 绘制刻度和标签,刻度在轴线的左侧,标签在轴线的右侧
for (int i = 0; i < mTickCount; i++) {
float tickValue = mMinValue + i * mTickSpacing;
float tickPosition = height - ((tickValue - mMinValue) / mValueRange) * (height - 2 * textPadding - 40) - textPadding - 20;
canvas.drawLine(0, tickPosition, tickLength, tickPosition, mTickPaint);
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float baseline = tickPosition - (fontMetrics.top + fontMetrics.bottom) / 2;
// 数值要格式化一位小数
String tickValueStr = String.format("%.1f", tickValue);
//文本x:轴线的右侧+文本与轴线的间距+文本的宽度
float textWidth = mTextPaint.measureText(tickValueStr);
canvas.drawText(tickValueStr, tickLength + textPadding + textWidth, baseline, mTextPaint);
}
}
}
/**
* 设置刻度数量
*
* @param count
*/
public void setTickCount(int count) {
mTickCount = count;
mTickSpacing = mValueRange / (mTickCount - 1);
invalidate();
}
/**
* 设置最小值
*
* @param minValue
*/
public void setMinValue(float minValue) {
mMinValue = minValue;
mValueRange = mMaxValue - mMinValue;
mTickSpacing = mValueRange / (mTickCount - 1);
invalidate();
}
/**
* 设置最大值
*
* @param maxValue
*/
public void setMaxValue(float maxValue) {
mMaxValue = maxValue;
mValueRange = mMaxValue - mMinValue;
mTickSpacing = mValueRange / (mTickCount - 1);
invalidate();
}
/**
* 设置轴线颜色
*
* @param color
*/
public void setAxisColor(String color){
mAxisPaint.setColor(Color.parseColor(color));
mTickPaint.setColor(Color.parseColor(color));
mTextPaint.setColor(Color.parseColor(color));
invalidate();
}
/**
* 设置标签字体大小
*/
public void setTextSize(int size){
mTextPaint.setTextSize(size);
invalidate();
}
}
三、使用方法
在XML布局文件中引用VerticalAxisView:
<com.example.VerticalAxisView
android:id="@+id/vertical_axis_view"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
在Java代码中获取VerticalAxisView并进行相关属性设置:
java
VerticalAxisView verticalAxisView = (VerticalAxisView) findViewById(R.id.vertical_axis_view);
// 设置刻度数量
verticalAxisView.setTickCount(5);
// 设置最大值和最小值
verticalAxisView.setMinValue(0f);
verticalAxisView.setMaxValue(100f);
// 设置轴线颜色
verticalAxisView.setAxisColor("#0000ff");
// 设置标签字体大小
verticalAxisView.setTextSize(16);
四、注意事项
- 在绘制刻度和标签时,要根据当前值与最小值的差值以及总值范围来计算出位置;
- 文本在轴线左侧和右侧时,位置计算方法不同,需要分别处理;
- 在修改属性后,需要调用invalidate()方法来触发View的重绘。
五、总结
自定义View是Android开发中比较常见的需求之一,在实现时需要注意计算位置和属性变更后的重绘等问题。本文主要介绍了如何创建一个垂直轴线视图,并提供了设置刻度数量、最大值和最小值、轴线颜色以及标签字体大小等相关属性的方法,希望对大家学习自定义View有所帮助。