Creating Custom Bar Chart Views in Android

When implementing charts in Android applications, developers often rely on third-party libraries. However, for simple charting requirements, using a full-featured library might be overkill. This guide demonstrates how to create a custom bar chart view from scratch.

For complex charting needs, consider established libraries like:

  • MPAndroidChart
  • hellocharts-android
  • XCL-Charts
  • Android-Charts

Let's explore how to build a custom bar chart view in Android.

Basic Bar Chart Implementation

The following example shows a simple bar chart implementation:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import java.util.List;

public class CustomBarChart extends View {
    private float chartWidth;
    private float chartHeight;
    private float barWidth;
    private float spacing;
    private float maxDataValue;
    private List<BarData> dataItems;
    
    private Paint barPaint;
    private Paint textPaint;
    private RectF barRect;
    
    public CustomBarChart(Context context) {
        this(context, null);
    }
    
    public CustomBarChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }
    
    private void initialize() {
        barPaint = new Paint();
        barPaint.setAntiAlias(true);
        barRect = new RectF();
        
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextAlign(Paint.Align.CENTER);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        
        chartWidth = widthSize - getPaddingLeft() - getPaddingRight();
        chartHeight = heightSize - getPaddingTop() - getPaddingBottom();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        if (dataItems == null || dataItems.isEmpty()) return;
        
        float availableWidth = chartWidth - spacing;
        barWidth = (availableWidth / dataItems.size()) * 0.7f;
        
        float maxBarHeight = chartHeight * 0.8f;
        float bottomY = chartHeight - 50;
        
        for (int i = 0; i < dataItems.size(); i++) {
            BarData item = dataItems.get(i);
            float barHeight = (item.getValue() / maxDataValue) * maxBarHeight;
            float leftX = getPaddingLeft() + spacing + (i * (barWidth + spacing));
            float topY = bottomY - barHeight;
            
            // Draw bar
            barPaint.setColor(item.getColor());
            barRect.set(leftX, topY, leftX + barWidth, bottomY);
            canvas.drawRect(barRect, barPaint);
            
            // Draw label
            textPaint.setColor(Color.BLACK);
            textPaint.setTextSize(12);
            canvas.drawText(item.getLabel(), leftX + barWidth/2, bottomY + 20, textPaint);
            
            // Draw value
            canvas.drawText(String.valueOf(item.getValue()), 
                          leftX + barWidth/2, topY - 10, textPaint);
        }
    }
    
    public void setData(List<BarData> data) {
        this.dataItems = data;
        
        // Calculate max value for scaling
        maxDataValue = 0;
        for (BarData item : data) {
            if (item.getValue() > maxDataValue) {
                maxDataValue = item.getValue();
            }
        }
        
        invalidate();
    }
    
    public static class BarData {
        private int color;
        private String label;
        private float value;
        
        public BarData(int color, String label, float value) {
            this.color = color;
            this.label = label;
            this.value = value;
        }
        
        public int getColor() { return color; }
        public String getLabel() { return label; }
        public float getValue() { return value; }
    }
}

Multi-Layer Bar Chart

For more complex visualization, you might need a multi-layer bar chart. Here's an implementation that supports stacked bars:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import java.util.List;

public class StackedBarChart extends View {
    private float chartWidth;
    private float chartHeight;
    private float barWidth;
    private float spacing;
    private float maxDataValue;
    
    private Paint barPaint;
    private Paint textPaint;
    private RectF barRect;
    
    private List<StackedBarData> dataItems;
    
    public StackedBarChart(Context context) {
        this(context, null);
    }
    
    public StackedBarChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }
    
    private void initialize() {
        barPaint = new Paint();
        barPaint.setAntiAlias(true);
        barRect = new RectF();
        
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setTextAlign(Paint.Align.CENTER);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        
        chartWidth = widthSize - getPaddingLeft() - getPaddingRight();
        chartHeight = heightSize - getPaddingTop() - getPaddingBottom();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        if (dataItems == null || dataItems.isEmpty()) return;
        
        float availableWidth = chartWidth - spacing;
        barWidth = (availableWidth / dataItems.size()) * 0.7f;
        
        float maxBarHeight = chartHeight * 0.8f;
        float bottomY = chartHeight - 50;
        
        for (int i = 0; i < dataItems.size(); i++) {
            StackedBarData stackedBar = dataItems.get(i);
            List<BarSegment> segments = stackedBar.getSegments();
            
            float currentHeight = 0;
            for (BarSegment segment : segments) {
                float segmentHeight = (segment.getValue() / maxDataValue) * maxBarHeight;
                float topY = bottomY - currentHeight - segmentHeight;
                
                // Draw segment
                barPaint.setColor(segment.getColor());
                barRect.set(getPaddingLeft() + spacing + (i * (barWidth + spacing)), 
                            topY,
                            getPaddingLeft() + spacing + (i * (barWidth + spacing)) + barWidth,
                            bottomY - currentHeight);
                canvas.drawRect(barRect, barPaint);
                
                // Draw value
                textPaint.setColor(Color.WHITE);
                textPaint.setTextSize(10);
                canvas.drawText(String.valueOf(segment.getValue()), 
                              getPaddingLeft() + spacing + (i * (barWidth + spacing)) + barWidth/2,
                              topY + segmentHeight/2, textPaint);
                
                currentHeight += segmentHeight;
            }
            
            // Draw label
            textPaint.setColor(Color.BLACK);
            textPaint.setTextSize(12);
            canvas.drawText(stackedBar.getLabel(), 
                          getPaddingLeft() + spacing + (i * (barWidth + spacing)) + barWidth/2,
                          bottomY + 20, textPaint);
        }
    }
    
    public void setData(List<StackedBarData> data) {
        this.dataItems = data;
        
        // Calculate max value for scaling
        maxDataValue = 0;
        for (StackedBarData stackedBar : data) {
            float totalValue = 0;
            for (BarSegment segment : stackedBar.getSegments()) {
                totalValue += segment.getValue();
            }
            if (totalValue > maxDataValue) {
                maxDataValue = totalValue;
            }
        }
        
        invalidate();
    }
    
    public static class StackedBarData {
        private String label;
        private List<BarSegment> segments;
        
        public StackedBarData(String label, List<BarSegment> segments) {
            this.label = label;
            this.segments = segments;
        }
        
        public String getLabel() { return label; }
        public List<BarSegment> getSegments() { return segments; }
    }
    
    public static class BarSegment {
        private int color;
        private float value;
        
        public BarSegment(int color, float value) {
            this.color = color;
            this.value = value;
        }
        
        public int getColor() { return color; }
        public float getValue() { return value; }
    }
}

Implementation Tips

  • Use anti-aliased painting for smoother graphics
  • Implement proper measurement and layout handling
  • Add animation effects for better user experience
  • Consider adding touch interaction for data exploration
  • Optimize performance by avoiding unnecessary redraws

Tags: Android Custom View Bar Chart Data Visualization UI Components

Posted on Tue, 26 May 2026 19:27:33 +0000 by shadypalm88