自定义View
当Android系统内置的View无法实现我们的需求时,自定义View可以很好的解决我们的问题,例如现在想做出一个动态的环形统计图效果,只靠系统内置VIew是无法完成的。写一个自定义View类,首先要让它继承自View,然后重写两个函数,onMeasure()、onDraw()。onMeasure负责对当前View的尺寸进行测量,onDraw()负责把当前这个View绘制出来,当然还必须写上构造函数
public class RingChart extends View {
public RingChart(Context context) {
super(context);
}
public RingChart(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
在activity的oncreate方法中创建并实例化这个自定义View,并添加到frameLayout帧布局中,这样就可以在活动中显示
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.layout);
frameLayout.addView(new RingChart(this));
}
}
模仿账单的统计图
绘制成圆环统计效果,我们可以使用drawArc实现,为每一段数据画一段扇形,最后用一个白色的更小的圆遮盖住扇形的中间一部分,这样就会有圆环的效果了,看一下最后的效果图:
/**
* drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
* drawArc() 是使用一个椭圆来描述弧形的。
* left, top, right, bottom 描述的是这个弧形所在的椭圆;
* startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度),
* sweepAngle 是弧形划过的角度;
* useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。
*/
那么我们的扇形数据类就需要以下几个属性
private String text; //标签文本
private float value; //各标签数值
private float Angle; //需要绘制的圆弧角度
private float CurrentStartAngle; //当前起始角度
private float Percentage; //百分比
private int color; //各部分颜色
扇形的每一段颜色通过先定义好的一个颜色数组去得到,当获得数据列表时,根据数据的条数去设置每一段扇形的颜色,这里先放了8个颜色,即数据列表中最多可以有八条,颜色列表可根据需要具体更改
public final static int[] COLORS = {
Color.parseColor("#FFD778"), Color.parseColor("#FEA3C2"),
Color.parseColor("#66DAD9"),Color.parseColor("#9092FF"),
Color.parseColor("#A0E491"),Color.parseColor("#6BD9AC"),
Color.parseColor("#75A2FF"),Color.parseColor("#6BD9AC")
};
虽然ViewData类中设置了很多属性,但我们真正传入的只有text和value,即标签和数据,其他的像总和、百分比、角度都可以通过计算得到,所以在绘制扇形之前还需要对数据进行以下处理
//初始化数据
private void initData(List<ViewData> mData){
if (mData == null || mData.size() == 0) {
return;
}
//根据数据条数确定颜色
for (int i = 0; i < mData.size(); i++) {
ViewData data = mData.get(i);
sumValue += data.getValue();
data.setColor(COLORS[i]);
}
//计算百分比和角度
float currentStartAngle = 0f; //设置起始角度为0
for (int i = 0; i < mData.size(); i++) {
ViewData data = mData.get(i);
//为每一段设置起始角度
data.setCurrentStartAngle(currentStartAngle);
//通过总和来计算百分比
float percentage = data.getValue() / sumValue;
//通过百分比来计算对应的角度
float angle = percentage * 360;
//设置数据
data.setPercentage(percentage);
data.setAngle(angle);
currentStartAngle += angle;
}
}
拿到数据之后我们就可以绘制扇形了,通过循环,为数据列表的每一个数据都绘制一段扇形,最终是一个圆形的样子
for (int i= 0; i < viewDataList.size(); i++){
ViewData data = viewDataList.get(i);
//设置画笔颜色
mPaint.setColor(data.getColor());
//绘制扇形
canvas.drawArc(left,top,right,bottom,data.getCurrentStartAngle(),data.getAngle(),true,mPaint);
}
绘制一个更小的圆遮挡,并在中间写上文字
//绘制一个更小的圆将扇形遮挡中间的部分,让效果看起来变成一个圆环
mPaint.setColor(Color.WHITE);
canvas.drawCircle(centerX,centerY, (float) (r/1.4),mPaint);
mPaint.setColor(Color.BLACK);
float width = mPaint.measureText(sumValue+"元",0,(sumValue+"元").length());
canvas.drawText(sumValue+"元",centerX - width/2,centerY+10,mPaint);
这样一个环形统计图的样子就出来了,再定义一个动画让它有动态效果,动画时间设了2秒,2秒之后动画加载完会显示默认显示效果
public class RingChartAnimation extends Animation{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 2.0f) {
for (int i = 0; i < viewDataList.size(); i++) {
ViewData data = viewDataList.get(i);
//通过总和来计算百分比
float percentage = data.getValue() / sumValue;
//通过百分比来计算对应的角度
float angle = percentage * 360;
//根据插入时间来计算角度
angle = angle * interpolatedTime;
data.setAngle(angle);
}
} else {//默认显示效果
for (int i = 0; i < viewDataList.size(); i++) {
//通过总和来计算百分比
ViewData data = viewDataList.get(i);
float percentage = data.getValue() / sumValue;
//通过百分比来计算对应的角度
float angle = percentage * 360;
data.setPercentage(percentage);
data.setAngle(angle);
}
}
invalidate();
}
}
对于如何设置圆环上文本标签的位置,这里学习了 自定义View之扇形统计图,写的非常详细,完全解决了我的困扰
那我们在写完所有绘制过程之后,就可以在MainActivity中传数据了,方便起见,这里就直接定义一个数据列表传入了,运行之后就是我们的最后效果图了
public class MainActivity extends AppCompatActivity {
RingChart ringChart;
List<ViewData> viewDataList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.layout);
ringChart = new RingChart(this);
frameLayout.addView(ringChart);
initData();
ringChart.setData(viewDataList);
}
public void initData(){
viewDataList.add(new ViewData("饮食",800));
viewDataList.add(new ViewData("出行",300));
viewDataList.add(new ViewData("购物",500));
viewDataList.add(new ViewData("娱乐",200));
viewDataList.add(new ViewData("住房",200));
viewDataList.add(new ViewData("其他",100));
}
}
通过这个例子可以看出自定义View的可扩展性还是很强大的,复杂图形j经过拆分、组合、加上动画效果,会让普通的界面有更好的观感