先上效果图
注:绿边是自定义的,可以随意修改。如果需要修改,参见 PieChart 文件中的 TODO 说明
1、把代码复制到自己的项目中,用到的布局资源、源码在附录中。因为需要几个文件,因此,建议放到一个文件夹下。因为不会上传文件,只能把代码复制出来放到附录里。
2、布局代码:
<?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:orientation="vertical" >
<LinearLayout android:id="@+id/pie" android:layout_width="match_parent" android:layout_height="180dp" android:background="#FFFFFF" android:orientation="horizontal">
<com.example.lenovo.demo.view.cakeview.PieChart android:id="@+id/piechart" android:layout_width="140dp" android:layout_height="140dp" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp"/>
<ListView android:id="@+id/listView_plan_detail" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" android:divider="@null" android:listSelector="#00000000">
</ListView>
</LinearLayout>
</LinearLayout>
3、实现代码:
为了方便管理颜色,写了个全局变量,实际使用,可以根据自己的方式写。如果也要写全局变量,注意颜色数组长度,防止角标越界
package com.example.lenovo.demo;
public class PARAM {
// 颜色
public static final int[] COLORS = new int[]{0xfff5be63, 0xfff4a21a, 0xff9bcade, 0xff11a0c2, 0xff007aa1,0xffef7417};
}
主代码:
package com.example.lenovo.demo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.example.lenovo.demo.view.cakeview.PieChart;
import com.example.lenovo.demo.view.cakeview.TitleValueEntity;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ArrayList<TitleValueEntity> listData;
private PieChart pie_chart;
private ListView listView;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pie_chart = (PieChart) findViewById(R.id.piechart);
listView = (ListView) findViewById(R.id.listView_plan_detail);
listData = new ArrayList<TitleValueEntity>();
initView();
adapter = new MyAdapter();
listView.setAdapter(adapter);
pie_chart.setData(listData);
pie_chart.invalidate();
}
private void initView() {
listData.add(new TitleValueEntity("未分配", 10, 0, "code-1"));
listData.add(new TitleValueEntity("游戏", 35, 0, "code-2"));
listData.add(new TitleValueEntity("购物", 30, 0, "code-3"));
listData.add(new TitleValueEntity("旅游", 5, 0, "code-4"));
listData.add(new TitleValueEntity("吃饭", 20, 0, "code-5"));
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return listData.size();
}
@Override
public Object getItem(int position) {
return listData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_piechat, null);
TextView tv = (TextView) view.findViewById(R.id.tv_pie_data);
view.findViewById(R.id.color).setBackgroundColor(PARAM.COLORS[position]);
TitleValueEntity item = listData.get(position);
String title = item.getTitle();
String code = item.getCode();
tv.setText(title + code + ": "
+ item.getValue() + "%");
return view;
}
}
}
注:上面的code-1这些值,可以为空,这里为了有扩展性,先加上了。
2、PieChart:用于显示饼状图
package com.example.lenovo.demo.view.cakeview;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.animation.DecelerateInterpolator;
import com.example.lenovo.demo.PARAM;
import java.util.List;
public class PieChart extends RoundChart {
protected List<TitleValueEntity> data;
private ValueAnimator cakeValueAnimator;
private float curAngle;
private float obj2Float(Object o) {
return ((Number) o).floatValue();
}
public PieChart(Context context) {
super(context);
PropertyValuesHolder angleValues = PropertyValuesHolder.ofFloat(
"angle", 0f, 360f);
cakeValueAnimator = ValueAnimator.ofPropertyValuesHolder(angleValues);
cakeValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float mAngle = obj2Float(animation.getAnimatedValue("angle"));
curAngle = mAngle;
}
});
cakeValueAnimator.setDuration(1500);
cakeValueAnimator.setRepeatCount(0);
cakeValueAnimator.setInterpolator(new DecelerateInterpolator());
cakeValueAnimator.setRepeatMode(ValueAnimator.RESTART);
}
public PieChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int rect = super.getWidth() > super.getHeight() ? super.getHeight()
: super.getWidth();
longitudeLength = (int) ((rect / 2f) * 0.90);
position = new Point((int) (getWidth() / 2f), (int) (getHeight() / 2f));
drawCircle(canvas);
drawData(canvas);
}
protected void drawCircle(Canvas canvas) {
Paint mPaintCircleBorder = new Paint();
mPaintCircleBorder.setStyle(Style.STROKE);
mPaintCircleBorder.setStrokeWidth(1);
mPaintCircleBorder.setAntiAlias(true);
//TODO 这个颜色是设置饼状图最外面的圆环颜色。这里为了显示明显,设置成绿色
mPaintCircleBorder.setColor(0xff00ff00);
mPaintCircleBorder.setStyle(Style.FILL);
canvas.drawCircle(position.x, position.y, longitudeLength + 15,
mPaintCircleBorder);
mPaintCircleBorder.setStyle(Style.STROKE);
mPaintCircleBorder.setColor(circleBorderColor);
canvas.drawCircle(position.x, position.y, longitudeLength,
mPaintCircleBorder);
}
protected void drawData(Canvas canvas) {
if (null != data) {
float sum = 0;
for (int i = 0; i < data.size(); i++) {
sum = sum + data.get(i).getValue();
}
Paint mPaintFill = new Paint();
mPaintFill.setStyle(Style.FILL);
mPaintFill.setAntiAlias(true);
Paint mPaintBorder = new Paint();
mPaintBorder.setStyle(Style.STROKE);
mPaintBorder.setColor(longitudeColor);
mPaintBorder.setAntiAlias(true);
int offset = -90;
for (int j = 0; j < data.size(); j++) {
TitleValueEntity e = data.get(j);
if (e.getValue() == 0)
continue;
mPaintFill.setColor(PARAM.COLORS[j]);
RectF oval = new RectF(position.x - longitudeLength, position.y
- longitudeLength, position.x + longitudeLength, position.y
+ longitudeLength);
int sweep = Math.round(e.getValue() / sum * 360f);
canvas.drawArc(oval, offset, sweep, true, mPaintFill);
canvas.drawArc(oval, offset, sweep, true, mPaintBorder);
offset = offset + sweep;
}
}
}
public List<TitleValueEntity> getData() {
return data;
}
public void setData(List<TitleValueEntity> data) {
this.data = data;
}
}
3、AbstractChart
package com.example.lenovo.demo.view.cakeview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
/** * 所有图表对象的基类 */
public abstract class AbstractChart extends View {
/** * 默认控件是否显示边框 */
public static final boolean DEFAULT_DISPLAY_BORDER = Boolean.FALSE;
/** * 默认经线刻度字体颜色 */
public static final int DEFAULT_BORDER_COLOR = Color.LTGRAY;
public static final float DEFAULT_BORDER_WIDTH = 1f;
/** * 控件是否显示边框 */
protected boolean displayBorder = DEFAULT_DISPLAY_BORDER;
/** * 图边框的颜色 */
protected int borderColor = DEFAULT_BORDER_COLOR;
protected float borderWidth = DEFAULT_BORDER_WIDTH;
public AbstractChart(Context context) {
super(context);
}
public AbstractChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AbstractChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (this.displayBorder) {
drawBorder(canvas);
}
}
/** * 绘制边框 */
protected void drawBorder(Canvas canvas) {
Paint mPaint = new Paint();
mPaint.setColor(borderColor);
mPaint.setStrokeWidth(borderWidth);
mPaint.setStyle(Style.STROKE);
// draw a rectangle
canvas.drawRect(borderWidth / 2, borderWidth / 2, super.getWidth()
- borderWidth / 2, super.getHeight() - borderWidth / 2, mPaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
}
4、RoundChart
package com.example.lenovo.demo.view.cakeview;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.util.AttributeSet;
public class RoundChart extends AbstractChart {
/** * 默认图表标题 */
public static final String DEFAULT_TITLE = "Round Chart";
/** * 默认是否显示经线 */
public static final boolean DEFAULT_DISPLAY_LONGITUDE = true;
/** * 默认半径长度 */
public static final int DEFAULT_LONGITUDE_LENGTH = 55;
/** * 默认经线颜色 */
public static final int DEFAULT_LONGITUDE_COLOR = Color.WHITE;
/** * 默认圆弧的颜色 */
public static final int DEFAULT_CIRCLE_BORDER_COLOR = Color.WHITE;
/** * 默认绘图中心位置 */
public static final Point DEFAULT_POSITION = new Point(0, 0);
/** * 绘图中心位置 */
protected Point position = DEFAULT_POSITION;
/** * 半径长度 */
protected float longitudeLength = DEFAULT_LONGITUDE_LENGTH;
/** * 经线颜色 */
protected int longitudeColor = DEFAULT_LONGITUDE_COLOR;
/** * 圆弧的颜色 */
protected int circleBorderColor = DEFAULT_CIRCLE_BORDER_COLOR;
public RoundChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public RoundChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundChart(Context context) {
super(context);
}
}
5、TitleValueEntity:一个bean文件
package com.example.lenovo.demo.view.cakeview;
public class TitleValueEntity {
private String title;
private int value;
private int color;
private String code;
public TitleValueEntity(String title, int value, int color, String code) {
this.title = title;
this.value = value;
this.color = color;
this.code = code;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
6、右侧listview的条目布局
<?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:orientation="horizontal" >
<LinearLayout android:layout_width="match_parent" android:layout_height="25dp" android:orientation="horizontal" android:gravity="center_vertical" >
<TextView android:id="@+id/color" android:layout_width="10dp" android:layout_height="10dp" android:layout_marginRight="5dp" />
<TextView android:id="@+id/tv_pie_data" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="#666666" />
</LinearLayout>
</LinearLayout>