在开发中我们需要自定义view的地方很多,我们在开发中需要重写view来实现项目中各式各样的需求,其实自定义view并没有想象中那么难,按照步骤,一步步实现就可以。
这里我实现了一个价格变动折线图,先上图看看效果
这里把实现步骤做一下讲解,
1. 首先创建自己的自定义类,并继承view,然后重写构造方法,使得我们的xml文件可以通过自定义属性实现布局上面的改变;
2. 然后为自定义画笔,测量间距做准备,定义相关变量
//xy坐标轴颜色
private int xylinecolor = 0xffe2e2e2;
//xy坐标轴宽度
private int xylinewidth = dpToPx(1);
//xy坐标轴文字颜色
private int xytextcolor = 0xff7e7e7e;
//xy坐标轴文字大小
private int xytextsize = spToPx(12);
//折线图中折线的颜色
private int linecolor = 0xffff6600;
//x轴各个坐标点水平间距
private int interval = dpToPx(50);
//背景颜色
private int bgcolor = 0xffffffff;
//是否在ACTION_UP时,根据速度进行自滑动,没有要求,建议关闭,过于占用GPU
private boolean isScroll = false;
//绘制XY轴坐标对应的画笔
private Paint xyPaint;
//绘制XY轴的文本对应的画笔
private Paint xyTextPaint;
//画折线对应的画笔
private Paint linePaint;
private Paint linePaintXu;//虚线画笔
//画分割线对应的画笔
private Paint lineVerticalPaint;
//x轴底线对应的画笔
private Paint bottomLinePaint;
//可以渐变的画笔属性
private LinearGradient lg;
private int width;
private int height;
//x轴的原点坐标
private int xOri;
//y轴的原点坐标
private int yOri;
//第一个点X的坐标
private float xInit;
//第一个点对应的最大Y坐标
private float maxXInit;
//第一个点对应的最小X坐标
private float minXInit;
private Context context;
//x轴坐标对应的数据
private List<String> xValue = new ArrayList<>();
//y轴坐标对应的数据
private List<Integer> yValue = new ArrayList<>();
//折线对应的数据
private Map<String, Float> value = new HashMap<>();
//点击的点对应的X轴的第几个点,默认1
private int selectIndex = 0;
//X轴刻度文本对应的最大矩形,为了选中时,在x轴文本画的框框大小一致
private Rect xValueRect;
//速度检测器
private VelocityTracker velocityTracker;
3. 做初始化操作,xml文件自定义属性的初始化,画笔的初始化
/**
* 初始化
*
* @param context
* @param attrs
* @param defStyleAttr
*/
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.chartView, defStyleAttr, 0);
int count = array.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.chartView_xylinecolor://xy坐标轴颜色
xylinecolor = array.getColor(attr, xylinecolor);
break;
case R.styleable.chartView_xylinewidth://xy坐标轴宽度
xylinewidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xylinewidth, getResources().getDisplayMetrics()));
break;
case R.styleable.chartView_xytextcolor://xy坐标轴文字颜色
xytextcolor = array.getColor(attr, xytextcolor);
break;
case R.styleable.chartView_xytextsize://xy坐标轴文字大小
xytextsize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xytextsize, getResources().getDisplayMetrics()));
break;
case R.styleable.chartView_linecolor://折线图中折线的颜色
linecolor = array.getColor(attr, linecolor);
break;
case R.styleable.chartView_interval://x轴各个坐标点水平间距
interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getDisplayMetrics()));
break;
case R.styleable.chartView_bgcolor: //背景颜色
bgcolor = array.getColor(attr, bgcolor);
break;
case R.styleable.chartView_isScroll://是否在ACTION_UP时,根据速度进行自滑动
isScroll = array.getBoolean(attr, isScroll);
break;
}
}
array.recycle();
}
/**
* 初始化画笔
*/
private void initPaint() {
xyPaint = new Paint();
xyPaint.setAntiAlias(true);
xyPaint.setStrokeWidth(xylinewidth);
xyPaint.setStrokeCap(Paint.Cap.ROUND);
xyPaint.setColor(xylinecolor);
xyTextPaint = new Paint();
xyTextPaint.setAntiAlias(true);
xyTextPaint.setTextSize(xytextsize);
xyTextPaint.setStrokeCap(Paint.Cap.ROUND);
xyTextPaint.setColor(xytextcolor);
xyTextPaint.setStyle(Paint.Style.STROKE);
linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(xylinewidth);
linePaint.setStrokeCap(Paint.Cap.ROUND);
linePaint.setColor(linecolor);
linePaint.setStyle(Paint.Style.STROKE);
linePaintXu = new Paint();
linePaintXu.setAntiAlias(true);
linePaintXu.setStrokeWidth(xylinewidth);
linePaintXu.setColor(linecolor);
linePaintXu.setStyle(Paint.Style.STROKE);
lineVerticalPaint = new Paint();
lineVerticalPaint.setAntiAlias(true);
lineVerticalPaint.setStyle(Paint.Style.FILL);
bottomLinePaint = new Paint();
bottomLinePaint.setAntiAlias(true);
bottomLinePaint.setStrokeWidth(xylinewidth);
bottomLinePaint.setStrokeCap(Paint.Cap.ROUND);
bottomLinePaint.setColor(0xffffe7d6);
bottomLinePaint.setStyle(Paint.Style.STROKE);
}
4. 实现onlayout,onmeasure,ondraw方法,通过测量,确定布局区域,绘制出折线图,这里需要确定各个位置的描点,首先是xy轴的坐标点,然后就是折线图趋势走势的点,然后就是绘制textview给予文本提示,思路大致是这样,然后就是代码实现了
类似于图表的在github上面有开源的第三方框架,用起来也是比较方便,自定义这个图标只是为了练习下自定义view,锻炼动手能力,熟悉自定义view的一般过程,当然有时候也需要view跟随手势变化做重绘,自定义view要注意内存的优化,不要发生内存抖动,更要避免发生OOM。
完整代码github链接