由于项目中需要用到饼图,用MpAndroidChart,当饼图部分占比很小时,描述文字重叠,所以自己重新绘制了饼图,并提供饼图各部分的点击监听,效果图如下:
绘制饼图的类:
package com.karoline.views.bars;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.karoline.utils.SizeUtils;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ${Karoline} on 2017/6/14.
*/
public class AssetKcPie extends View {//继承View类
private Context mContext;
private Paint textPaint;
private Paint arcPaint;
private Paint linePaint;
private WeakReference bitmapBuffer;
private Canvas bitmapCanvas;
private float distance;
private float radius;
private int barWidth,barHeight;
private List datas;
private List angleSEs;
private List lengedRectes;
private OnSelectedListener mListener;
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
float actionX = event.getX(); //点击点的坐标
float actionY = event.getY();
double distance = Math.sqrt(Math.pow(Math.abs(actionX-barWidth/2),2)+
Math.pow(Math.abs(actionY-barHeight/2),2));
double angle = Math.atan((actionY-barHeight/2) /(actionX-barWidth/2)) /3.14 * 180 - 90;
float X = barWidth/2,Y=(barHeight-lengedHeight)/2;
if(actionX > X && actionY X && actionY>Y) {
angle = 90+angle;
}else if (actionX < X && actionY>Y) {
angle = 270-angle;
}else if (actionX < X && actionY angleSEs.get(i).getStartAngle() && angle dataS,float total){
this.datas = dataS;
this.totalNum = total;
invalidate();
}
public void setLenged(){ //设置是否绘制控件下方的描述
isLengedVisible = true;
lengedHeight = (int) SizeUtils.dp2px(mContext,32);
setMeasuredDimension(onWidthMeasure(getMeasuredWidth()),onHeightMeasure(getMeasuredHeight()));
}
private void drawLenged(){ //绘制控件下面的描述
lengedRectes = new ArrayList<>();
Rect rect = new Rect();
float lengedX = distance;
float lengedY = barHeight - lengedHeight + distance;
float totalWidth;
for(int i = 0;i barWidth){//判断描述的长度是否大于一行
lengedY = lengedY +distance;
lengedX = distance;
}
bitmapCanvas.drawRect(lengedX,lengedY-distance/2,lengedX+distance/2,lengedY ,arcPaint);//绘制描述的颜色方块
rectF.left = lengedX;
rectF.top = lengedY-distance;
lengedX = lengedX + distance/2 + 4;
bitmapCanvas.drawText(datas.get(i).getDesc(),lengedX,lengedY,textPaint);//绘制描述的颜色文字
lengedX = lengedX + rect.width() + distance;
rectF.right = lengedX;
rectF.bottom = lengedY + distance;
lengedRectes.add(rectF);//将描述说在的Recf存起来备用(注意放大点击的热区)
}
}
bitmapCanvas.save();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int Width = onWidthMeasure(widthMeasureSpec); //计算控件的宽度
int height = onHeightMeasure(heightMeasureSpec);//计算控件的高度
setMeasuredDimension(Width,height);
}
//当控件的宽度,高度发生变化是调用
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int width = getMeasuredWidth();
int hight = getMeasuredHeight();
if (bitmapBuffer == null
|| (bitmapBuffer.get().getWidth() != width)
|| (bitmapBuffer.get().getHeight() != hight)) {
if (width > 0 && hight > 0) {
bitmapBuffer = new WeakReference(Bitmap.createBitmap(width, hight, Bitmap.Config.ARGB_4444));
bitmapCanvas = new Canvas(bitmapBuffer.get());
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
bitmapBuffer.get().eraseColor(Color.TRANSPARENT);//绘制时先擦除画板上的内容(用于更新界面)
if(datas != null && datas.size() > 0 ){
if(isLengedVisible){
drawLenged();
}
angleSEs = new ArrayList<>();
RectF arcRect = new RectF(barWidth/2-radius,(barHeight-lengedHeight)/2 - radius,
barWidth/2+radius,(barHeight-lengedHeight)/2 + radius);//圆形所在的RectF,圆心与控件中心重叠
float startAngle = -90,sweepAngle = 0;
float perAngle = totalNum/360;//每一度的数量
String desc;
float lineAngle;
Rect rect = new Rect();
for(int i=0;ibarWidth/2) { //当指示线位于饼图右侧时,在右侧绘制第二条指示线及文字
bitmapCanvas.drawLine(lineEndX,lineEndY,lineEndX+distance/2,lineEndY,linePaint);
bitmapCanvas.drawText(desc,lineEndX+distance/2+4,lineEndY+rect.height()/2,textPaint);
}else {//当指示线位于饼图左侧时,在左侧绘制第二条指示线及文字
bitmapCanvas.drawLine(lineEndX,lineEndY,lineEndX-distance/2,lineEndY,linePaint);
bitmapCanvas.drawText(desc,lineEndX-distance/2-4-rect.width(),lineEndY+rect.height()/2,textPaint);
}
}
private int onWidthMeasure(int width){
int mode = MeasureSpec.getMode(width);
int size = MeasureSpec.getSize(width);
if(mode == MeasureSpec.EXACTLY){
barWidth = size;
}else if(mode == MeasureSpec.AT_MOST){
barWidth = width - getPaddingLeft() - getPaddingRight();
}
return barWidth;
}
private int onHeightMeasure(int height){
int mode1 = MeasureSpec.getMode(height);
int size1 = MeasureSpec.getSize(height);
int minSize = (int) SizeUtils.dp2px(mContext,120);
if(mode1 == MeasureSpec.EXACTLY){
barHeight = size1;
}else { //当控件的高度为wrapContet时计算控件的高度
if(datas != null && datas.size()>0){
barHeight = (int) radius*2 + lengedHeight + (int) distance*2 + (int) distance*datas.size() ;
}else {
barHeight = minSize - getPaddingTop() - getPaddingBottom();
}
}
return barHeight;
}
public void setOnSelectedListener(OnSelectedListener l){
mListener = l;
}
public class AngleSE{
private float startAngle;
private float sweepAngle;
public AngleSE(float startAngle, float sweepAngle) {
this.startAngle = startAngle;
this.sweepAngle = sweepAngle;
}
public float getStartAngle() {
return startAngle;
}
public float getSweepAngle() {
return sweepAngle;
}
}
public interface OnSelectedListener{ //点击监听接口
void onSelected(int position);
}
}
调用示例:
package com.karoline.uiviews;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.karoline.R;
import com.karoline.codelibrary.BaseToolBarActivity;
import com.karoline.views.bars.AssetKcData;
import com.karoline.views.bars.AssetKcPie;
import com.karoline.views.bars.AssetLenged;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
public class AsssetKcActivity extends BaseToolBarActivity implements AssetKcPie.OnSelectedListener {
@BindView(R.id.assetkc_desc)
TextView assetkcDesc;
@BindView(R.id.assetkc_chart)
AssetKcPie assetkcPie;
@BindView(R.id.assetkc_desc1)
TextView assetkcDesc1;
@BindView(R.id.assetkc_chart1)
AssetKcPie assetkcChart1;
@BindView(R.id.assetkc_lenged)
AssetLenged assetkcLenged;
private int mode = 1;
private List perColorList;
private List descList = new ArrayList<>();
private List kcDatas;
@Override
protected int getContentView() {
return R.layout.activity_assset_kc;
}
@Override
protected void init(Bundle savedInstanceState) {
setTitle("库存概览");
assetkcPie.setOnSelectedListener(this);//添加监听
assetkcPie.setLenged();
assetkcDesc1.setText("");
kcDatas = new ArrayList<>();
perColorList = new ArrayList<>();
perColorList.add(Color.parseColor("#f36c60"));
perColorList.add(Color.parseColor("#fba6c8"));
perColorList.add(Color.parseColor("#7986cb"));
perColorList.add(Color.parseColor("#4fc3f7"));
perColorList.add(Color.parseColor("#cfd8dc"));
perColorList.add(Color.parseColor("#4db6ac"));
perColorList.add(Color.parseColor("#aed581"));
perColorList.add(Color.parseColor("#fff176"));
perColorList.add(Color.parseColor("#ffb74d"));
updateView();
}
private void updateView() {
List list = new ArrayList<>();
list.add(new AssetKcData(Color.rgb(205, 92, 92), "工器具", 318243f));
list.add(new AssetKcData(Color.rgb(255, 193, 37), "消耗品", 674937f));
list.add(new AssetKcData(Color.parseColor("#cfd8dc"), "设施设备工装", 212664f));
list.add(new AssetKcData(Color.rgb(10, 149, 237), "原材料", 35420464f));
assetkcPie.setData(list, 318243 + 674937 + 212664 + 35420464);
}
@Override
public void onSelected(int position) {
List list = new ArrayList<>();
switch (position) {
case 0:
assetkcDesc1.setText("工器具金额");
assetkcChart1.setVisibility(View.VISIBLE);
list.clear();
list.add(new AssetKcData(perColorList.get(0), "电动工具", 38189.16f));
list.add(new AssetKcData(perColorList.get(1), "手动工具", 25459.44f));
list.add(new AssetKcData(perColorList.get(2), "仪器仪表", 12729.72f));
list.add(new AssetKcData(perColorList.get(3), "照明灯具", 12729.72f));
list.add(new AssetKcData(perColorList.get(4), "通讯工具", 25459.44f));
list.add(new AssetKcData(perColorList.get(5), "杂项工具", 12729.72f));
descList.clear();
descList.add("电动工具");
descList.add("手动工具");
descList.add("仪器仪表");
descList.add("照明灯具");
descList.add("通讯工具");
descList.add("杂项工具");
assetkcChart1.setData(list, 127297.20f);
assetkcLenged.setData(perColorList, descList);
break;
case 1:
assetkcDesc1.setText("消耗品金额");
assetkcChart1.setVisibility(View.VISIBLE);
list.clear();
list.add(new AssetKcData(perColorList.get(0), "刀具", 134987.4f));
list.add(new AssetKcData(perColorList.get(1), "防寒防汛", 53994.96f));
list.add(new AssetKcData(perColorList.get(2), "劳防用品", 80992.44f));
descList.clear();
descList.add("刀具");
descList.add("防寒防汛");
descList.add("劳防用品");
assetkcChart1.setData(list, 269974.8f);
assetkcLenged.setData(perColorList, descList);
break;
case 2:
assetkcDesc1.setText("设施设备工装金额");
assetkcChart1.setData(null, 100f);
assetkcLenged.setData(null, null);
break;
case 3:
assetkcDesc1.setText("原材料金额");
assetkcChart1.setVisibility(View.VISIBLE);
list.clear();
list.add(new AssetKcData(perColorList.get(0), "备品备件(专用)", 1416818.56f));
list.add(new AssetKcData(perColorList.get(1), "备品备件(通用)", 1558500.416f));
list.add(new AssetKcData(perColorList.get(2), "紧固件", 4250455.68f));
list.add(new AssetKcData(perColorList.get(3), "水暖配件", 708409.28f));
list.add(new AssetKcData(perColorList.get(4), "电器电料", 2833637.12f));
list.add(new AssetKcData(perColorList.get(5), "灯类", 2125227.84f));
list.add(new AssetKcData(perColorList.get(6), "辅料", 566727.424f));
list.add(new AssetKcData(perColorList.get(7), "材料", 283363.712f));
list.add(new AssetKcData(perColorList.get(8), "化工原料", 425045.568f));
descList.clear();
descList.add("备品备件(专用)");
descList.add("备品备件(通用)");
descList.add("紧固件");
descList.add("水暖配件");
descList.add("电器电料");
descList.add("灯类");
descList.add("辅料");
descList.add("材料");
descList.add("化工原料");
assetkcChart1.setData(list, 14168185.6f);
assetkcLenged.setData(perColorList, descList);
break;
}
}
}
附调用示例的xml文件: