| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- package hr.tacitus.graphs;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.RectF;
- import android.support.annotation.Nullable;
- import android.text.Layout;
- import android.text.StaticLayout;
- import android.text.TextPaint;
- import android.util.AttributeSet;
- import android.view.View;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * Created by Bero on 7.7.2017..
- */
- public class PieChartView extends View {
- class PieChartItem {
- float value;
- // color for this pie chart item
- Paint paint;
- // text
- String description;
- // cached value
- int descriptionWidth;
- float getAngle() {
- return value * 3.6f;
- }
- }
- private static float MAX_LEGEND_WIDTH_PERCENTAGE = 35f;
- private static float MAX_LEGEND_WIDTH_PX = 300;
- private static float LEGEND_PADDING_HORIZONTAL = 20f;
- private static float LEGEND_PADDING_TOP = 20f;
- private float titleTextSizeSp = 16.f;
- private boolean showLegend = true;
- private float legendTextSizeSp = 10.f;
- private float legendSquareSize = 20f;
- private float legendWidth;
- private float chartPadding = 64.f;
- private float chartTextSizeSp = 10.f;
- private String title = "This is a title";
- private List<Integer> colors;
- private TextPaint titlePaint;
- private TextPaint valuePaint;
- private TextPaint legendDescriptionPaint;
- private StaticLayout titleTextLayout;
- private RectF titleRect;
- private RectF legendRect;
- private RectF pieChartRect;
- private List<PieChartItem> items = new ArrayList<>();
- private boolean readyToDraw = false;
- public PieChartView(Context context) {
- super(context);
- init(null);
- }
- public PieChartView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(attrs);
- }
- public PieChartView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(attrs);
- }
- private void readAttributes(AttributeSet attributeSet) {
- TypedArray a = getContext().obtainStyledAttributes(attributeSet, R.styleable.PieChartView);
- if (a != null) {
- try {
- title = a.getString(R.styleable.PieChartView_title);
- showLegend = a.getBoolean(R.styleable.PieChartView_showLegend, true);
- } finally {
- a.recycle();
- }
- }
- }
- @Nullable
- private RectF getLegendRect() {
- if (showLegend) {
- float left = titleRect.right - legendWidth;
- float right = titleRect.right;
- float top = titleRect.bottom;
- float bottom = getBottom();
- return new RectF(left, top, right, bottom);
- }
- return null;
- }
- private RectF getPieChartRect() {
- float left = titleRect.left + chartPadding;
- float top = titleRect.top + chartPadding;
- float right = titleRect.right - chartPadding;
- if (showLegend)
- right -= legendRect.width();
- float bottom = getBottom() - getPaddingLeft() - chartPadding;
- RectF rectF = new RectF(left, top, right, bottom);
- // width must be equal to height
- if (rectF.width() != rectF.height()) {
- float width = Math.min(rectF.width(), rectF.height());
- left = rectF.centerX() - width / 2;
- right = left + width;
- top = rectF.centerY() - width / 2;
- bottom = top + width;
- rectF = new RectF(left, top, right, bottom);
- }
- return rectF;
- }
- private RectF getTitleRect() {
- return new RectF(getPaddingLeft(), getPaddingTop(),
- getWidth() - getPaddingLeft() - getPaddingRight(), 32.f);
- }
- private void initTitleText() {
- // int width = (int) titlePaint.measureText(title);
- titleTextLayout = new StaticLayout(title, titlePaint, (int) titleRect.width(),
- Layout.Alignment.ALIGN_CENTER, 1.0f, 0, false);
- }
- private void init(AttributeSet attributeSet) {
- if (attributeSet != null)
- readAttributes(attributeSet);
- titlePaint = new TextPaint();
- titlePaint.setAntiAlias(true);
- titlePaint.setTextSize(Utils.spsToPixels(titleTextSizeSp, getResources()));
- titlePaint.setColor(Color.BLACK);
- valuePaint = new TextPaint();
- valuePaint.setAntiAlias(true);
- valuePaint.setTextSize(Utils.spsToPixels(chartTextSizeSp, getResources()));
- valuePaint.setColor(Color.BLACK);
- legendDescriptionPaint = new TextPaint();
- legendDescriptionPaint.setAntiAlias(true);
- legendDescriptionPaint.setTextSize(Utils.spsToPixels(legendTextSizeSp, getResources()));
- }
- private void initRectangles() {
- titleRect = getTitleRect();
- if (showLegend) {
- updateLegendWidth();
- }
- legendRect = getLegendRect();
- pieChartRect = getPieChartRect();
- }
- private void drawTitle(Canvas canvas) {
- if (titleTextLayout != null)
- titleTextLayout.draw(canvas);
- }
- private void drawChart(Canvas canvas) {
- float startAngle = -90.f;
- for (PieChartItem item : items) {
- canvas.drawArc(pieChartRect, startAngle, item.getAngle(), true, item.paint);
- startAngle += item.getAngle();
- }
- startAngle = 90.f;
- for (PieChartItem item : items) {
- float angleMiddle = startAngle - item.getAngle() / 2;
- float r = pieChartRect.width() / 2;
- float x = (float) Math.cos(Math.toRadians(angleMiddle)) * r;
- float y = (float) Math.sin(Math.toRadians(angleMiddle)) * r;
- String text = Double.toString(item.value);
- // Depending on quadrant, adjust text.
- float textWidth = valuePaint.measureText(text);
- float left = x + pieChartRect.centerX();
- float top = pieChartRect.centerY() - y;
- // RectF textRect = new RectF(left, top, left + textWidth, top + valuePaint.getTextSize());
- canvas.drawText(Integer.toHexString(item.paint.getColor()), left, top, valuePaint);
- startAngle -= item.getAngle();
- }
- }
- private void drawLegend(Canvas canvas) {
- Paint p = new Paint();
- p.setColor(Color.RED);
- canvas.drawRect(legendRect, p);
- float left = legendRect.left + LEGEND_PADDING_HORIZONTAL;
- float top = legendRect.top + LEGEND_PADDING_TOP;
- // Draw color explanations
- for (int i = 0; i < items.size(); ++i) {
- PieChartItem item = items.get(i);
- canvas.drawRect(new RectF(left, top, left + legendSquareSize, top + legendSquareSize), item.paint);
- // canvas.drawText(Double.toString(item.value), left + 30f, top + 20f, valuePaint);
- StaticLayout staticLayout = new StaticLayout(item.description,
- valuePaint, Utils.textWidth(item.description, valuePaint, true),
- Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
- canvas.save();
- canvas.translate(left + (legendSquareSize * 1.5f), top);
- staticLayout.draw(canvas);
- canvas.restore();
- top += legendSquareSize * 1.5f;
- }
- }
- @Override
- public void onDraw(Canvas canvas) {
- if (!readyToDraw)
- return;
- drawTitle(canvas);
- drawChart(canvas);
- if (showLegend)
- drawLegend(canvas);
- }
- @Override
- public void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
- super.onSizeChanged(xNew, yNew, xOld, yOld);
- initRectangles();
- initTitleText();
- readyToDraw = true;
- }
- private void initColors(int count) {
- colors = new ArrayList<>();
- // 3 sets of 2 main colors - rb, rg, bg
- int maxValue = 220;
- int perSetGain = (int) (maxValue / (Math.ceil(count / 6.) + 1));
- float r = maxValue, g = 0, b = 0;
- for (int i = 0; i < count; ++i) {
- int color = Color.rgb((int) r, (int) g, (int) b);
- colors.add(color);
- r = Color.green(color);
- g = Color.blue(color);
- b = Color.red(color);
- if (i != 0 && (i + 1) % 3 == 0) {
- if (i == 2) {
- g = maxValue;
- } else {
- r -= perSetGain;
- }
- }
- }
- }
- private void createItems(float[] values) {
- for (int i = 0; i < values.length; ++i) {
- PieChartItem pieChartItem = new PieChartItem();
- pieChartItem.value = values[i];
- pieChartItem.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- pieChartItem.paint.setColor(colors.get(i));
- pieChartItem.description = Double.toString(values[i]);
- pieChartItem.descriptionWidth = Utils.textWidth(pieChartItem.description, legendDescriptionPaint, true);
- items.add(pieChartItem);
- }
- }
- private float getMaxLegendWidth() {
- float width = getWidth();
- float maxPercWidth = width * MAX_LEGEND_WIDTH_PERCENTAGE;
- return Math.min(maxPercWidth, MAX_LEGEND_WIDTH_PX);
- }
- /**
- * Calculates required legend width.
- */
- private void updateLegendWidth() {
- float maxWidth = getMaxLegendWidth();
- legendWidth = 0;
- for (PieChartItem item : items) {
- float width = item.descriptionWidth + 2 * LEGEND_PADDING_HORIZONTAL + legendSquareSize * 1.5f;
- if (width > legendWidth)
- legendWidth = width;
- if (legendWidth >= maxWidth) {
- legendWidth = maxWidth;
- break;
- }
- }
- }
- public void setValues(float[] values) {
- items.clear();
- initColors(values.length);
- createItems(values);
- initRectangles();
- invalidate();
- }
- }
|