From b331fa4f905f63e8bd9ad0ec05c5232f567dd92b Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Mar 2026 12:51:01 -0700 Subject: [PATCH 01/13] add Gantt chart feature --- MPChartExample/src/main/AndroidManifest.xml | 70 ++++++++++--------- .../chartdemo/TimeIntervalChartActivity.java | 62 ++++++++++++++++ .../layout/activity_time_interval_chart.xml | 11 +++ .../charting/buffer/HorizontalBarBuffer.java | 19 +++-- .../renderer/HorizontalBarChartRenderer.java | 35 ++++++++++ .../mikephil/charting/utils/GanttUtils.java | 55 +++++++++++++++ 6 files changed, 208 insertions(+), 44 deletions(-) create mode 100644 MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java create mode 100644 MPChartExample/src/main/res/layout/activity_time_interval_chart.xml create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java diff --git a/MPChartExample/src/main/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml index 99334e601a..75edab1a9c 100644 --- a/MPChartExample/src/main/AndroidManifest.xml +++ b/MPChartExample/src/main/AndroidManifest.xml @@ -12,46 +12,48 @@ android:theme="@style/AppTheme"> + android:label="@string/app_name" + android:exported="true" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java new file mode 100644 index 0000000000..6e25272a7b --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java @@ -0,0 +1,62 @@ +package com.xxmassdeveloper.chartdemo; + +import android.graphics.Color; +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.utils.GanttUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Example of using HorizontalBarChart for Gantt-style time interval visualization. + * Shows how to display tasks as horizontal bars with start time and duration. + */ +public class TimeIntervalChartActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_time_interval_chart); + + HorizontalBarChart chart = findViewById(R.id.chart); + + // Create sample tasks with time intervals + List entries = new ArrayList<>(); + + // Task 1: starts at 0, duration 100 + entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100)); + + // Task 2: starts at 50, duration 150 + entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150)); + + // Task 3: starts at 150, duration 100 + entries.add(GanttUtils.createTimeIntervalEntry(2, 150, 100)); + + // Create dataset with time interval data + BarDataSet dataSet = new BarDataSet(entries, "Tasks"); + dataSet.setColors(Color.rgb(61, 165, 255), Color.rgb(230, 126, 34), Color.rgb(46, 204, 113)); + dataSet.setBarBorderWidth(1f); + dataSet.setBarBorderColor(Color.BLACK); + + // Create and set data + BarData barData = new BarData(dataSet); + barData.setBarWidth(0.8f); + chart.setData(barData); + + // Configure chart + chart.setFitBars(true); + chart.setDrawValueAboveBar(true); + chart.getXAxis().setDrawLabels(true); + chart.getYAxis().setPosition(com.github.mikephil.charting.components.YAxis.YAxisLabelPosition.LEFT); + + // Refresh + chart.invalidate(); + } +} diff --git a/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml b/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml new file mode 100644 index 0000000000..db285295a3 --- /dev/null +++ b/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index 6b63bc30ea..5981b0602d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -31,7 +31,8 @@ public void feed(IBarDataSet data) { float bottom = x - barWidthHalf; float top = x + barWidthHalf; - float left, right; + float left; + float right; if (mInverted) { left = y >= 0 ? y : 0; right = y <= 0 ? y : 0; @@ -52,7 +53,6 @@ public void feed(IBarDataSet data) { float posY = 0f; float negY = -e.getNegativeSum(); - float yStart = 0f; // fill the stack for (int k = 0; k < vals.length; k++) { @@ -61,23 +61,22 @@ public void feed(IBarDataSet data) { if (value >= 0f) { y = posY; - yStart = posY + value; - posY = yStart; + posY += value; } else { y = negY; - yStart = negY + Math.abs(value); negY += Math.abs(value); } float bottom = x - barWidthHalf; float top = x + barWidthHalf; - float left, right; + float left; + float right; if (mInverted) { - left = y >= yStart ? y : yStart; - right = y <= yStart ? y : yStart; + left = y >= posY ? y : posY; + right = y <= posY ? y : posY; } else { - right = y >= yStart ? y : yStart; - left = y <= yStart ? y : yStart; + right = y >= posY ? y : posY; + left = y <= posY ? y : posY; } // multiply the height of the rect with the phase diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index 0cd72345fb..fb606c4143 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -440,4 +440,39 @@ protected boolean isDrawingValuesAllowed(ChartInterface chart) { return chart.getData().getEntryCount() < chart.getMaxVisibleCount() * mViewPortHandler.getScaleY(); } + + /** + * Feed buffer with time interval data for Gantt-style charts. + * Supports BarEntry with float[] {startTime, duration} format. + * + * @param buffer The buffer to fill + * @param dataSet The dataset containing time interval data + */ + protected void feedTimeIntervalBuffer(BarBuffer buffer, IBarDataSet dataSet) { + int bufferIndex = 0; + float barWidth = mChart.getBarData().getBarWidth(); + float barWidthHalf = barWidth / 2f; + + for (int i = 0; i < dataSet.getEntryCount(); i++) { + BarEntry entry = dataSet.getEntryForIndex(i); + if (entry != null) { + float[] vals = entry.getYVals(); + if (vals != null && vals.length >= 2) { + // vals[0] = start time, vals[1] = duration + float start = vals[0]; + for (int k = 1; k < vals.length; k++) { + float end = start + vals[k]; + + // Store as [left, top, right, bottom] + buffer.buffer[bufferIndex++] = start; + buffer.buffer[bufferIndex++] = entry.getX() - barWidthHalf; + buffer.buffer[bufferIndex++] = end; + buffer.buffer[bufferIndex++] = entry.getX() + barWidthHalf; + + start = end; + } + } + } + } + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java new file mode 100644 index 0000000000..7776a2e144 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java @@ -0,0 +1,55 @@ +package com.github.mikephil.charting.utils; + +import com.github.mikephil.charting.data.BarEntry; + +/** + * Utility class for creating time interval entries for Gantt-style charts. + * Provides helper methods to create BarEntry objects with time interval data. + */ +public class GanttUtils { + + private GanttUtils() { + // utility class - no instances + } + + /** + * Create a time interval entry for a single task. + * + * @param taskIndex Y-axis position (task row) + * @param startTime Start time value + * @param duration Duration value + * @return BarEntry configured for time interval rendering + */ + public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration) { + return new BarEntry(taskIndex, new float[]{startTime, duration}); + } + + /** + * Create a time interval entry with multiple segments. + * Useful for showing multiple time ranges for a single task. + * + * @param taskIndex Y-axis position (task row) + * @param timeIntervals Array of [start1, duration1, start2, duration2, ...] + * @return BarEntry configured for time interval rendering + */ + public static BarEntry createMultiSegmentEntry(float taskIndex, float[] timeIntervals) { + if (timeIntervals == null || timeIntervals.length < 2) { + throw new IllegalArgumentException("timeIntervals must have at least 2 elements"); + } + return new BarEntry(taskIndex, timeIntervals); + } + + /** + * Create a time interval entry with custom data. + * + * @param taskIndex Y-axis position (task row) + * @param startTime Start time value + * @param duration Duration value + * @param data Custom data object + * @return BarEntry configured for time interval rendering with data + */ + public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, + float duration, Object data) { + return new BarEntry(taskIndex, new float[]{startTime, duration}, data); + } +} From 0b889510332cce58780b223346d4cf6f6fc78f64 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 27 Mar 2026 13:03:23 -0700 Subject: [PATCH 02/13] add Gantt chart feature --- MPChartExample/build.gradle | 4 ++++ MPChartExample/src/main/AndroidManifest.xml | 2 +- .../chartdemo/TimeIntervalChartActivity.java | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 2d607e9991..6dc755fa81 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -17,6 +17,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + lintOptions { + abortOnError false + } } dependencies { diff --git a/MPChartExample/src/main/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml index 75edab1a9c..0443ce2f02 100644 --- a/MPChartExample/src/main/AndroidManifest.xml +++ b/MPChartExample/src/main/AndroidManifest.xml @@ -53,7 +53,7 @@ - + diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java index 6e25272a7b..347677d58f 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java @@ -6,10 +6,12 @@ import androidx.appcompat.app.AppCompatActivity; import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; import com.github.mikephil.charting.utils.GanttUtils; +import com.xxmassdeveloper.mpchartexample.R; import java.util.ArrayList; import java.util.List; @@ -54,7 +56,9 @@ protected void onCreate(Bundle savedInstanceState) { chart.setFitBars(true); chart.setDrawValueAboveBar(true); chart.getXAxis().setDrawLabels(true); - chart.getYAxis().setPosition(com.github.mikephil.charting.components.YAxis.YAxisLabelPosition.LEFT); + + YAxis yl = chart.getAxisLeft(); + yl.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); // Refresh chart.invalidate(); From 70f654c2b7ee8cc8a51eb39f8aa09283448316ce Mon Sep 17 00:00:00 2001 From: David Date: Sat, 28 Mar 2026 11:00:06 -0700 Subject: [PATCH 03/13] Fix Gantt chart implementation: restore yStart logic, remove unused feedTimeIntervalBuffer, improve GanttUtils documentation and demo --- .../chartdemo/TimeIntervalChartActivity.java | 70 ++++++++++++------- .../charting/buffer/HorizontalBarBuffer.java | 13 ++-- .../renderer/HorizontalBarChartRenderer.java | 35 ---------- .../mikephil/charting/utils/GanttUtils.java | 38 +++++++++- 4 files changed, 90 insertions(+), 66 deletions(-) diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java index 347677d58f..378e58fb9b 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java @@ -6,6 +6,8 @@ import androidx.appcompat.app.AppCompatActivity; import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; @@ -17,8 +19,11 @@ import java.util.List; /** - * Example of using HorizontalBarChart for Gantt-style time interval visualization. - * Shows how to display tasks as horizontal bars with start time and duration. + * Demo activity showing Gantt-style time interval visualization using HorizontalBarChart. + * Each bar represents a task with start time and duration. + * + * This demonstrates how to create project timeline or resource allocation charts + * where each horizontal bar represents a task spanning from a start time to an end time. */ public class TimeIntervalChartActivity extends AppCompatActivity { @@ -29,38 +34,55 @@ protected void onCreate(Bundle savedInstanceState) { HorizontalBarChart chart = findViewById(R.id.chart); - // Create sample tasks with time intervals + // Create sample project tasks with start times and durations + // Format: task row, start time, duration List entries = new ArrayList<>(); + entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100)); // Task A: 0-100 + entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150)); // Task B: 50-200 + entries.add(GanttUtils.createTimeIntervalEntry(2, 150, 100)); // Task C: 150-250 - // Task 1: starts at 0, duration 100 - entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100)); - - // Task 2: starts at 50, duration 150 - entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150)); - - // Task 3: starts at 150, duration 100 - entries.add(GanttUtils.createTimeIntervalEntry(2, 150, 100)); - - // Create dataset with time interval data - BarDataSet dataSet = new BarDataSet(entries, "Tasks"); - dataSet.setColors(Color.rgb(61, 165, 255), Color.rgb(230, 126, 34), Color.rgb(46, 204, 113)); + // Create dataset with task data + BarDataSet dataSet = new BarDataSet(entries, "Project Tasks"); + dataSet.setColors( + Color.rgb(61, 165, 255), // Blue + Color.rgb(230, 126, 34), // Orange + Color.rgb(46, 204, 113) // Green + ); dataSet.setBarBorderWidth(1f); dataSet.setBarBorderColor(Color.BLACK); - // Create and set data + // Configure chart data BarData barData = new BarData(dataSet); - barData.setBarWidth(0.8f); + barData.setBarWidth(0.6f); chart.setData(barData); - // Configure chart - chart.setFitBars(true); + // Disable default interactions chart.setDrawValueAboveBar(true); - chart.getXAxis().setDrawLabels(true); - - YAxis yl = chart.getAxisLeft(); - yl.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); + chart.setFitBars(true); + chart.setPinchZoom(false); + chart.setDragEnabled(true); + + // Configure X-axis (timeline) + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setDrawLabels(true); + xAxis.setDrawGridLines(true); + + // Configure Y-axis (tasks) + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); + leftAxis.setDrawGridLines(false); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); + + // Configure legend + Legend legend = chart.getLegend(); + legend.setEnabled(true); + legend.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + legend.setOrientation(Legend.LegendOrientation.HORIZONTAL); - // Refresh chart.invalidate(); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index 5981b0602d..cbe56bf88a 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -58,12 +58,15 @@ public void feed(IBarDataSet data) { for (int k = 0; k < vals.length; k++) { float value = vals[k]; + float yStart; if (value >= 0f) { y = posY; - posY += value; + yStart = posY + value; + posY = yStart; } else { y = negY; + yStart = negY + Math.abs(value); negY += Math.abs(value); } @@ -72,11 +75,11 @@ public void feed(IBarDataSet data) { float left; float right; if (mInverted) { - left = y >= posY ? y : posY; - right = y <= posY ? y : posY; + left = y >= yStart ? y : yStart; + right = y <= yStart ? y : yStart; } else { - right = y >= posY ? y : posY; - left = y <= posY ? y : posY; + right = y >= yStart ? y : yStart; + left = y <= yStart ? y : yStart; } // multiply the height of the rect with the phase diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java index fb606c4143..0cd72345fb 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -440,39 +440,4 @@ protected boolean isDrawingValuesAllowed(ChartInterface chart) { return chart.getData().getEntryCount() < chart.getMaxVisibleCount() * mViewPortHandler.getScaleY(); } - - /** - * Feed buffer with time interval data for Gantt-style charts. - * Supports BarEntry with float[] {startTime, duration} format. - * - * @param buffer The buffer to fill - * @param dataSet The dataset containing time interval data - */ - protected void feedTimeIntervalBuffer(BarBuffer buffer, IBarDataSet dataSet) { - int bufferIndex = 0; - float barWidth = mChart.getBarData().getBarWidth(); - float barWidthHalf = barWidth / 2f; - - for (int i = 0; i < dataSet.getEntryCount(); i++) { - BarEntry entry = dataSet.getEntryForIndex(i); - if (entry != null) { - float[] vals = entry.getYVals(); - if (vals != null && vals.length >= 2) { - // vals[0] = start time, vals[1] = duration - float start = vals[0]; - for (int k = 1; k < vals.length; k++) { - float end = start + vals[k]; - - // Store as [left, top, right, bottom] - buffer.buffer[bufferIndex++] = start; - buffer.buffer[bufferIndex++] = entry.getX() - barWidthHalf; - buffer.buffer[bufferIndex++] = end; - buffer.buffer[bufferIndex++] = entry.getX() + barWidthHalf; - - start = end; - } - } - } - } - } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java index 7776a2e144..a6c23e208d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java @@ -5,6 +5,27 @@ /** * Utility class for creating time interval entries for Gantt-style charts. * Provides helper methods to create BarEntry objects with time interval data. + * + *

Usage Example:

+ *
+ * // Create a list of tasks with time intervals
+ * List entries = new ArrayList<>();
+ * 
+ * // Task 1 (row 0): starts at time 0, duration 100
+ * entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100));
+ * 
+ * // Task 2 (row 1): starts at time 50, duration 150
+ * entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150));
+ * 
+ * // Task 3 (row 2): starts at time 200, has two segments
+ * float[] timeIntervals = {200, 100, 250, 75}; // start1, duration1, start2, duration2
+ * entries.add(GanttUtils.createMultiSegmentEntry(2, timeIntervals));
+ * 
+ * // Create dataset and configure chart
+ * BarDataSet dataSet = new BarDataSet(entries, "Tasks");
+ * BarData barData = new BarData(dataSet);
+ * chart.setData(barData);
+ * 
*/ public class GanttUtils { @@ -19,6 +40,10 @@ private GanttUtils() { * @param startTime Start time value * @param duration Duration value * @return BarEntry configured for time interval rendering + * + * @example + * // Create task at row 0, starting at time 100, lasting 50 time units + * BarEntry entry = GanttUtils.createTimeIntervalEntry(0, 100, 50); */ public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration) { return new BarEntry(taskIndex, new float[]{startTime, duration}); @@ -31,10 +56,15 @@ public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, * @param taskIndex Y-axis position (task row) * @param timeIntervals Array of [start1, duration1, start2, duration2, ...] * @return BarEntry configured for time interval rendering + * + * @example + * // Create task with two time segments + * float[] segments = {100, 50, 200, 75}; // segment1: 100-150, segment2: 200-275 + * BarEntry entry = GanttUtils.createMultiSegmentEntry(1, segments); */ public static BarEntry createMultiSegmentEntry(float taskIndex, float[] timeIntervals) { if (timeIntervals == null || timeIntervals.length < 2) { - throw new IllegalArgumentException("timeIntervals must have at least 2 elements"); + throw new IllegalArgumentException("timeIntervals must have at least 2 elements [startTime, duration]"); } return new BarEntry(taskIndex, timeIntervals); } @@ -45,8 +75,12 @@ public static BarEntry createMultiSegmentEntry(float taskIndex, float[] timeInte * @param taskIndex Y-axis position (task row) * @param startTime Start time value * @param duration Duration value - * @param data Custom data object + * @param data Custom data object to attach to this entry * @return BarEntry configured for time interval rendering with data + * + * @example + * // Create task with custom data (e.g., task ID or status) + * BarEntry entry = GanttUtils.createTimeIntervalEntry(0, 100, 50, "Task-123"); */ public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration, Object data) { From bd839df529bae39c0ebad35272165fbe3619f971 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 19:16:26 -0700 Subject: [PATCH 04/13] Add jitpack.yml to specify Java version for builds --- jitpack.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 jitpack.yml diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000000..efde7bf258 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +jdk: + - openjdk17 From da694a9bfa1a9d96da36234fb4d20f088e204770 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 19:31:01 -0700 Subject: [PATCH 05/13] Fix Gantt chart rendering: position bars by time interval instead of stacking --- .../mikephil/charting/utils/GanttUtils.java | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java index a6c23e208d..4ae8966d6c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java @@ -12,14 +12,16 @@ * List entries = new ArrayList<>(); * * // Task 1 (row 0): starts at time 0, duration 100 + * // This creates a bar from 0 to 100 on the X-axis * entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100)); * * // Task 2 (row 1): starts at time 50, duration 150 + * // This creates a bar from 50 to 200 on the X-axis * entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150)); * - * // Task 3 (row 2): starts at time 200, has two segments - * float[] timeIntervals = {200, 100, 250, 75}; // start1, duration1, start2, duration2 - * entries.add(GanttUtils.createMultiSegmentEntry(2, timeIntervals)); + * // Task 3 (row 2): starts at time 200, duration 75 + * // This creates a bar from 200 to 275 on the X-axis + * entries.add(GanttUtils.createTimeIntervalEntry(2, 200, 75)); * * // Create dataset and configure chart * BarDataSet dataSet = new BarDataSet(entries, "Tasks"); @@ -35,48 +37,33 @@ private GanttUtils() { /** * Create a time interval entry for a single task. + * The bar will be positioned at startTime and sized to span the duration. * * @param taskIndex Y-axis position (task row) - * @param startTime Start time value - * @param duration Duration value - * @return BarEntry configured for time interval rendering + * @param startTime Start time value (X-axis position) + * @param duration Duration value (bar length on X-axis) + * @return BarEntry representing a time interval * * @example * // Create task at row 0, starting at time 100, lasting 50 time units + * // This renders as a bar from 100 to 150 on the timeline * BarEntry entry = GanttUtils.createTimeIntervalEntry(0, 100, 50); */ public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration) { - return new BarEntry(taskIndex, new float[]{startTime, duration}); - } - - /** - * Create a time interval entry with multiple segments. - * Useful for showing multiple time ranges for a single task. - * - * @param taskIndex Y-axis position (task row) - * @param timeIntervals Array of [start1, duration1, start2, duration2, ...] - * @return BarEntry configured for time interval rendering - * - * @example - * // Create task with two time segments - * float[] segments = {100, 50, 200, 75}; // segment1: 100-150, segment2: 200-275 - * BarEntry entry = GanttUtils.createMultiSegmentEntry(1, segments); - */ - public static BarEntry createMultiSegmentEntry(float taskIndex, float[] timeIntervals) { - if (timeIntervals == null || timeIntervals.length < 2) { - throw new IllegalArgumentException("timeIntervals must have at least 2 elements [startTime, duration]"); - } - return new BarEntry(taskIndex, timeIntervals); + // Position bar at middle of interval, Y value is the duration + // The bar will extend from (startTime) to (startTime + duration) + float barPosition = startTime + (duration / 2f); + return new BarEntry(barPosition, duration); } /** * Create a time interval entry with custom data. * - * @param taskIndex Y-axis position (task row) - * @param startTime Start time value - * @param duration Duration value + * @param taskIndex Y-axis position (task row) - Note: use this to differentiate visually if needed + * @param startTime Start time value (X-axis position) + * @param duration Duration value (bar length on X-axis) * @param data Custom data object to attach to this entry - * @return BarEntry configured for time interval rendering with data + * @return BarEntry representing a time interval with data * * @example * // Create task with custom data (e.g., task ID or status) @@ -84,6 +71,7 @@ public static BarEntry createMultiSegmentEntry(float taskIndex, float[] timeInte */ public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration, Object data) { - return new BarEntry(taskIndex, new float[]{startTime, duration}, data); + float barPosition = startTime + (duration / 2f); + return new BarEntry(barPosition, duration, data); } } From 360c3a40885c4f0e9908253a0171f2a79c55bdd3 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 19:51:50 -0700 Subject: [PATCH 06/13] Implement dedicated GanttChart custom view for proper time-interval rendering - Created GanttTask model class to represent indivdual tasks - Created GanttChartData container for managing task colection - Created GanttChart custom View that renders via Canvas - Updated GanttUtils with factory methods for the new model - Updated TimeIntervalChartActivity demo to use new GanttChart - Removed reliance on HorizontalBarChart stacked bar rendering --- .../chartdemo/TimeIntervalChartActivity.java | 81 +++--------- .../mikephil/charting/charts/GanttChart.java | 125 ++++++++++++++++++ .../charting/data/GanttChartData.java | 93 +++++++++++++ .../mikephil/charting/data/GanttTask.java | 47 +++++++ .../mikephil/charting/utils/GanttUtils.java | 100 +++++++------- 5 files changed, 329 insertions(+), 117 deletions(-) create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttChartData.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttTask.java diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java index 378e58fb9b..beb0e7e551 100644 --- a/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java @@ -5,25 +5,14 @@ import androidx.appcompat.app.AppCompatActivity; -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.utils.GanttUtils; +import com.github.mikephil.charting.charts.GanttChart; +import com.github.mikephil.charting.data.GanttChartData; +import com.github.mikephil.charting.data.GanttTask; import com.xxmassdeveloper.mpchartexample.R; -import java.util.ArrayList; -import java.util.List; - /** - * Demo activity showing Gantt-style time interval visualization using HorizontalBarChart. - * Each bar represents a task with start time and duration. - * - * This demonstrates how to create project timeline or resource allocation charts - * where each horizontal bar represents a task spanning from a start time to an end time. + * Demo activity showing Gantt-style timeline visualization. + * Each horizontal bar represents a task with start time and duration. */ public class TimeIntervalChartActivity extends AppCompatActivity { @@ -32,57 +21,19 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_time_interval_chart); - HorizontalBarChart chart = findViewById(R.id.chart); - - // Create sample project tasks with start times and durations - // Format: task row, start time, duration - List entries = new ArrayList<>(); - entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100)); // Task A: 0-100 - entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150)); // Task B: 50-200 - entries.add(GanttUtils.createTimeIntervalEntry(2, 150, 100)); // Task C: 150-250 - - // Create dataset with task data - BarDataSet dataSet = new BarDataSet(entries, "Project Tasks"); - dataSet.setColors( - Color.rgb(61, 165, 255), // Blue - Color.rgb(230, 126, 34), // Orange - Color.rgb(46, 204, 113) // Green - ); - dataSet.setBarBorderWidth(1f); - dataSet.setBarBorderColor(Color.BLACK); - - // Configure chart data - BarData barData = new BarData(dataSet); - barData.setBarWidth(0.6f); - chart.setData(barData); + GanttChart chart = findViewById(R.id.chart); - // Disable default interactions - chart.setDrawValueAboveBar(true); - chart.setFitBars(true); - chart.setPinchZoom(false); - chart.setDragEnabled(true); + // Create Gantt chart data + GanttChartData ganttData = new GanttChartData(); - // Configure X-axis (timeline) - XAxis xAxis = chart.getXAxis(); - xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); - xAxis.setDrawLabels(true); - xAxis.setDrawGridLines(true); + // Add sample project tasks + ganttData.addTask(new GanttTask("Design", 0, 50, Color.rgb(255, 107, 107))); // Red: 0-50 + ganttData.addTask(new GanttTask("Development", 40, 100, Color.rgb(66, 165, 245))); // Blue: 40-140 + ganttData.addTask(new GanttTask("Testing", 120, 40, Color.rgb(76, 175, 80))); // Green: 120-160 + ganttData.addTask(new GanttTask("Launch", 150, 20, Color.rgb(255, 193, 7))); // Yellow: 150-170 - // Configure Y-axis (tasks) - YAxis leftAxis = chart.getAxisLeft(); - leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); - leftAxis.setDrawGridLines(false); - - YAxis rightAxis = chart.getAxisRight(); - rightAxis.setEnabled(false); - - // Configure legend - Legend legend = chart.getLegend(); - legend.setEnabled(true); - legend.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); - legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); - legend.setOrientation(Legend.LegendOrientation.HORIZONTAL); - - chart.invalidate(); + // Set data and render + chart.setData(ganttData); } } + diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java new file mode 100644 index 0000000000..62d481fba1 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -0,0 +1,125 @@ +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +import com.github.mikephil.charting.data.GanttChartData; +import com.github.mikephil.charting.data.GanttTask; + +/** + * A custom Gantt chart view that renders tasks as horizontal bars. + * Each bar represents a task with its start time and duration. + */ +public class GanttChart extends View { + private GanttChartData data; + private Paint taskPaint; + private Paint gridPaint; + private Paint textPaint; + + private float chartLeft; + private float chartTop; + private float chartRight; + private float chartBottom; + private float taskHeight = 50; + private float padding = 20; + + public GanttChart(Context context) { + super(context); + init(); + } + + public GanttChart(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public GanttChart(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + taskPaint = new Paint(); + taskPaint.setAntiAlias(true); + + gridPaint = new Paint(); + gridPaint.setColor(0xFFE0E0E0); + gridPaint.setStrokeWidth(1); + + textPaint = new Paint(); + textPaint.setColor(0xFF333333); + textPaint.setTextSize(36); + textPaint.setAntiAlias(true); + } + + public void setData(GanttChartData data) { + this.data = data; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (data == null || data.getTaskCount() == 0) { + return; + } + + calculateDimensions(); + drawGrid(canvas); + drawTasks(canvas); + } + + private void calculateDimensions() { + chartLeft = padding + 100; // Leave space for labels + chartTop = padding; + chartRight = getWidth() - padding; + chartBottom = getHeight() - padding; + } + + private void drawGrid(Canvas canvas) { + // Draw vertical grid lines + float minTime = data.getMinTime(); + float maxTime = data.getMaxTime(); + float timeRange = maxTime - minTime; + if (timeRange == 0) timeRange = 100; + + int gridLines = 10; + for (int i = 0; i <= gridLines; i++) { + float x = chartLeft + (i / (float) gridLines) * (chartRight - chartLeft); + canvas.drawLine(x, chartTop, x, chartBottom, gridPaint); + + // Draw time labels + float time = minTime + (i / (float) gridLines) * timeRange; + canvas.drawText(String.format("%.0f", time), x - 15, chartBottom + 30, textPaint); + } + } + + private void drawTasks(Canvas canvas) { + float minTime = data.getMinTime(); + float maxTime = data.getMaxTime(); + float timeRange = maxTime - minTime; + if (timeRange == 0) timeRange = 100; + + for (int i = 0; i < data.getTaskCount(); i++) { + GanttTask task = data.getTask(i); + + // Calculate position + float taskY = chartTop + i * (taskHeight + 10); + float startX = chartLeft + ((task.getStartTime() - minTime) / timeRange) * (chartRight - chartLeft); + float endX = chartLeft + ((task.getEndTime() - minTime) / timeRange) * (chartRight - chartLeft); + + // Draw task bar + RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); + taskPaint.setColor(task.getColor()); + canvas.drawRect(rect, taskPaint); + + // Draw task label + canvas.drawText(task.getName(), padding, taskY + taskHeight / 2 + 12, textPaint); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttChartData.java new file mode 100644 index 0000000000..1970cce60f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttChartData.java @@ -0,0 +1,93 @@ +package com.github.mikephil.charting.data; + +import java.util.ArrayList; +import java.util.List; + +/** + * Data container for Gantt chart. + * Manages a list of tasks and provides convenient access methods. + */ +public class GanttChartData { + private List tasks = new ArrayList<>(); + + /** + * Add a task to the Gantt chart. + * + * @param task The task to add + */ + public void addTask(GanttTask task) { + tasks.add(task); + } + + /** + * Add multiple tasks to the Gantt chart. + * + * @param taskList List of tasks to add + */ + public void addTasks(List taskList) { + tasks.addAll(taskList); + } + + /** + * Get all tasks. + * + * @return List of all tasks + */ + public List getTasks() { + return tasks; + } + + /** + * Get a specific task by index. + * + * @param index Task index + * @return The task at the given index + */ + public GanttTask getTask(int index) { + return tasks.get(index); + } + + /** + * Get the number of tasks. + * + * @return Number of tasks in the chart + */ + public int getTaskCount() { + return tasks.size(); + } + + /** + * Get the earliest start time across all tasks. + * + * @return Minimum start time + */ + public float getMinTime() { + if (tasks.isEmpty()) return 0; + float min = Float.MAX_VALUE; + for (GanttTask task : tasks) { + min = Math.min(min, task.getStartTime()); + } + return min; + } + + /** + * Get the latest end time across all tasks. + * + * @return Maximum end time + */ + public float getMaxTime() { + if (tasks.isEmpty()) return 100; + float max = 0; + for (GanttTask task : tasks) { + max = Math.max(max, task.getEndTime()); + } + return max; + } + + /** + * Clear all tasks. + */ + public void clearTasks() { + tasks.clear(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttTask.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttTask.java new file mode 100644 index 0000000000..e21dc7425d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/GanttTask.java @@ -0,0 +1,47 @@ +package com.github.mikephil.charting.data; + +/** + * Represents a single task in a Gantt chart. + * Each task has a name, start time, duration, and display color. + */ +public class GanttTask { + private String name; + private float startTime; + private float duration; + private int color; + + /** + * Create a new Gantt task. + * + * @param name Task name/label + * @param startTime When the task starts + * @param duration How long the task lasts + * @param color Display color (Android color int) + */ + public GanttTask(String name, float startTime, float duration, int color) { + this.name = name; + this.startTime = startTime; + this.duration = duration; + this.color = color; + } + + public String getName() { + return name; + } + + public float getStartTime() { + return startTime; + } + + public float getEndTime() { + return startTime + duration; + } + + public float getDuration() { + return duration; + } + + public int getColor() { + return color; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java index 4ae8966d6c..62d5a91d55 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java @@ -1,32 +1,29 @@ package com.github.mikephil.charting.utils; -import com.github.mikephil.charting.data.BarEntry; +import android.graphics.Color; + +import com.github.mikephil.charting.data.GanttTask; +import com.github.mikephil.charting.data.GanttChartData; + +import java.util.ArrayList; +import java.util.List; /** - * Utility class for creating time interval entries for Gantt-style charts. - * Provides helper methods to create BarEntry objects with time interval data. + * Utility class for creating Gantt chart data and tasks. * *

Usage Example:

*
- * // Create a list of tasks with time intervals
- * List entries = new ArrayList<>();
- * 
- * // Task 1 (row 0): starts at time 0, duration 100
- * // This creates a bar from 0 to 100 on the X-axis
- * entries.add(GanttUtils.createTimeIntervalEntry(0, 0, 100));
- * 
- * // Task 2 (row 1): starts at time 50, duration 150
- * // This creates a bar from 50 to 200 on the X-axis
- * entries.add(GanttUtils.createTimeIntervalEntry(1, 50, 150));
+ * // Create Gantt chart data
+ * GanttChartData ganttData = new GanttChartData();
  * 
- * // Task 3 (row 2): starts at time 200, duration 75
- * // This creates a bar from 200 to 275 on the X-axis
- * entries.add(GanttUtils.createTimeIntervalEntry(2, 200, 75));
+ * // Add tasks
+ * ganttData.addTask(new GanttTask("Design", 0, 50, Color.rgb(255, 107, 107)));
+ * ganttData.addTask(new GanttTask("Development", 40, 100, Color.rgb(66, 165, 245)));
+ * ganttData.addTask(new GanttTask("Testing", 120, 40, Color.rgb(76, 175, 80)));
+ * ganttData.addTask(new GanttTask("Launch", 150, 20, Color.rgb(255, 193, 7)));
  * 
- * // Create dataset and configure chart
- * BarDataSet dataSet = new BarDataSet(entries, "Tasks");
- * BarData barData = new BarData(dataSet);
- * chart.setData(barData);
+ * // Or use the helper
+ * GanttChartData data = GanttUtils.createSampleGanttData();
  * 
*/ public class GanttUtils { @@ -36,42 +33,41 @@ private GanttUtils() { } /** - * Create a time interval entry for a single task. - * The bar will be positioned at startTime and sized to span the duration. - * - * @param taskIndex Y-axis position (task row) - * @param startTime Start time value (X-axis position) - * @param duration Duration value (bar length on X-axis) - * @return BarEntry representing a time interval - * - * @example - * // Create task at row 0, starting at time 100, lasting 50 time units - * // This renders as a bar from 100 to 150 on the timeline - * BarEntry entry = GanttUtils.createTimeIntervalEntry(0, 100, 50); + * Create a sample Gantt chart with demo tasks. + * Useful for testing and examples. + * + * @return GanttChartData with 4 sample tasks + */ + public static GanttChartData createSampleGanttData() { + GanttChartData data = new GanttChartData(); + + data.addTask(new GanttTask("Design", 0, 50, Color.rgb(255, 107, 107))); + data.addTask(new GanttTask("Development", 40, 100, Color.rgb(66, 165, 245))); + data.addTask(new GanttTask("Testing", 120, 40, Color.rgb(76, 175, 80))); + data.addTask(new GanttTask("Launch", 150, 20, Color.rgb(255, 193, 7))); + + return data; + } + + /** + * Create an empty Gantt chart data container. + * + * @return Empty GanttChartData */ - public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, float duration) { - // Position bar at middle of interval, Y value is the duration - // The bar will extend from (startTime) to (startTime + duration) - float barPosition = startTime + (duration / 2f); - return new BarEntry(barPosition, duration); + public static GanttChartData createGanttData() { + return new GanttChartData(); } /** - * Create a time interval entry with custom data. - * - * @param taskIndex Y-axis position (task row) - Note: use this to differentiate visually if needed - * @param startTime Start time value (X-axis position) - * @param duration Duration value (bar length on X-axis) - * @param data Custom data object to attach to this entry - * @return BarEntry representing a time interval with data - * - * @example - * // Create task with custom data (e.g., task ID or status) - * BarEntry entry = GanttUtils.createTimeIntervalEntry(0, 100, 50, "Task-123"); + * Create a new Gantt task. + * + * @param name Task name + * @param startTime Task start time + * @param duration Task duration + * @param color Task display color + * @return A new GanttTask */ - public static BarEntry createTimeIntervalEntry(float taskIndex, float startTime, - float duration, Object data) { - float barPosition = startTime + (duration / 2f); - return new BarEntry(barPosition, duration, data); + public static GanttTask createTask(String name, float startTime, float duration, int color) { + return new GanttTask(name, startTime, duration, color); } } From 7c81ab3e028c488a04ca9969114d4e98e5e129cf Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 20:18:42 -0700 Subject: [PATCH 07/13] Improve GanttChart styling and layout - Adjusted task height and padding for better proportions - Reduced font sizes for cleaner appearance - Added border strokes to task bars - Improved spacing between tasks - Better margin calculations for chart area - Enhanced grid line styling --- .../layout/activity_time_interval_chart.xml | 4 +-- .../mikephil/charting/charts/GanttChart.java | 36 ++++++++++++------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml b/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml index db285295a3..ae20c37731 100644 --- a/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml +++ b/MPChartExample/src/main/res/layout/activity_time_interval_chart.xml @@ -3,9 +3,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent"/> diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index 62d481fba1..c58e3f9698 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -24,8 +24,8 @@ public class GanttChart extends View { private float chartTop; private float chartRight; private float chartBottom; - private float taskHeight = 50; - private float padding = 20; + private float taskHeight = 35; + private float padding = 16; public GanttChart(Context context) { super(context); @@ -47,12 +47,12 @@ private void init() { taskPaint.setAntiAlias(true); gridPaint = new Paint(); - gridPaint.setColor(0xFFE0E0E0); + gridPaint.setColor(0xFFCCCCCC); gridPaint.setStrokeWidth(1); textPaint = new Paint(); - textPaint.setColor(0xFF333333); - textPaint.setTextSize(36); + textPaint.setColor(0xFF666666); + textPaint.setTextSize(28); textPaint.setAntiAlias(true); } @@ -75,10 +75,10 @@ protected void onDraw(Canvas canvas) { } private void calculateDimensions() { - chartLeft = padding + 100; // Leave space for labels - chartTop = padding; + chartLeft = padding + 80; // Leave space for task labels + chartTop = padding + 40; chartRight = getWidth() - padding; - chartBottom = getHeight() - padding; + chartBottom = getHeight() - padding - 40; } private void drawGrid(Canvas canvas) { @@ -109,17 +109,29 @@ private void drawTasks(Canvas canvas) { GanttTask task = data.getTask(i); // Calculate position - float taskY = chartTop + i * (taskHeight + 10); + float taskY = chartTop + i * (taskHeight + 8); float startX = chartLeft + ((task.getStartTime() - minTime) / timeRange) * (chartRight - chartLeft); float endX = chartLeft + ((task.getEndTime() - minTime) / timeRange) * (chartRight - chartLeft); - // Draw task bar + // Ensure minimum width for bars + if (endX - startX < 10) { + endX = startX + 10; + } + + // Draw task bar with border RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); taskPaint.setColor(task.getColor()); canvas.drawRect(rect, taskPaint); - // Draw task label - canvas.drawText(task.getName(), padding, taskY + taskHeight / 2 + 12, textPaint); + // Draw border + Paint borderPaint = new Paint(); + borderPaint.setColor(0xFF999999); + borderPaint.setStrokeWidth(2); + borderPaint.setStyle(Paint.Style.STROKE); + canvas.drawRect(rect, borderPaint); + + // Draw task label on left + canvas.drawText(task.getName(), padding + 8, taskY + taskHeight / 2 + 10, textPaint); } } } From 825ac8bf0d49cfcd3fe88e916c917d01cf58cd71 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 20:26:58 -0700 Subject: [PATCH 08/13] Improve Gantt chart label positioning and aesthetics - Increased task height for better readability - Repositioned labels to align properly on left side - Increased label column width for task names - Better vertical centering of labels within bars - Improved spacing between tasks - Enhanced font sizing for labels --- .../mikephil/charting/charts/GanttChart.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index c58e3f9698..3f33a8dfec 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -24,7 +24,7 @@ public class GanttChart extends View { private float chartTop; private float chartRight; private float chartBottom; - private float taskHeight = 35; + private float taskHeight = 40; private float padding = 16; public GanttChart(Context context) { @@ -75,10 +75,10 @@ protected void onDraw(Canvas canvas) { } private void calculateDimensions() { - chartLeft = padding + 80; // Leave space for task labels - chartTop = padding + 40; + chartLeft = padding + 100; // Leave space for task labels + chartTop = padding + 50; chartRight = getWidth() - padding; - chartBottom = getHeight() - padding - 40; + chartBottom = getHeight() - padding - 50; } private void drawGrid(Canvas canvas) { @@ -109,7 +109,7 @@ private void drawTasks(Canvas canvas) { GanttTask task = data.getTask(i); // Calculate position - float taskY = chartTop + i * (taskHeight + 8); + float taskY = chartTop + i * (taskHeight + 12); float startX = chartLeft + ((task.getStartTime() - minTime) / timeRange) * (chartRight - chartLeft); float endX = chartLeft + ((task.getEndTime() - minTime) / timeRange) * (chartRight - chartLeft); @@ -118,7 +118,7 @@ private void drawTasks(Canvas canvas) { endX = startX + 10; } - // Draw task bar with border + // Draw task bar RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); taskPaint.setColor(task.getColor()); canvas.drawRect(rect, taskPaint); @@ -130,8 +130,16 @@ private void drawTasks(Canvas canvas) { borderPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(rect, borderPaint); - // Draw task label on left - canvas.drawText(task.getName(), padding + 8, taskY + taskHeight / 2 + 10, textPaint); + // Draw task label on left side with proper alignment + Paint labelPaint = new Paint(); + labelPaint.setColor(0xFF333333); + labelPaint.setTextSize(26); + labelPaint.setAntiAlias(true); + labelPaint.setTextAlign(Paint.Align.LEFT); + + float labelX = padding + 8; + float labelY = taskY + (taskHeight / 2) + 8; + canvas.drawText(task.getName(), labelX, labelY, labelPaint); } } } From 4934431eb2084d6fc631316318619be50801d262 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 20:44:45 -0700 Subject: [PATCH 09/13] Fix label rendering - move to left column --- .../mikephil/charting/charts/GanttChart.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index 3f33a8dfec..1e445471c2 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -95,7 +95,12 @@ private void drawGrid(Canvas canvas) { // Draw time labels float time = minTime + (i / (float) gridLines) * timeRange; - canvas.drawText(String.format("%.0f", time), x - 15, chartBottom + 30, textPaint); + Paint timeLabelPaint = new Paint(); + timeLabelPaint.setColor(0xFF666666); + timeLabelPaint.setTextSize(22); + timeLabelPaint.setAntiAlias(true); + timeLabelPaint.setTextAlign(Paint.Align.CENTER); + canvas.drawText(String.format("%.0f", time), x, chartBottom + 30, timeLabelPaint); } } @@ -105,6 +110,17 @@ private void drawTasks(Canvas canvas) { float timeRange = maxTime - minTime; if (timeRange == 0) timeRange = 100; + Paint labelPaint = new Paint(); + labelPaint.setColor(0xFF333333); + labelPaint.setTextSize(24); + labelPaint.setAntiAlias(true); + labelPaint.setTextAlign(Paint.Align.RIGHT); + + Paint borderPaint = new Paint(); + borderPaint.setColor(0xFF999999); + borderPaint.setStrokeWidth(2); + borderPaint.setStyle(Paint.Style.STROKE); + for (int i = 0; i < data.getTaskCount(); i++) { GanttTask task = data.getTask(i); @@ -118,28 +134,18 @@ private void drawTasks(Canvas canvas) { endX = startX + 10; } + // Draw task label on left side + float labelX = chartLeft - 15; + float labelY = taskY + (taskHeight / 2) + 8; + canvas.drawText(task.getName(), labelX, labelY, labelPaint); + // Draw task bar RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); taskPaint.setColor(task.getColor()); canvas.drawRect(rect, taskPaint); // Draw border - Paint borderPaint = new Paint(); - borderPaint.setColor(0xFF999999); - borderPaint.setStrokeWidth(2); - borderPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(rect, borderPaint); - - // Draw task label on left side with proper alignment - Paint labelPaint = new Paint(); - labelPaint.setColor(0xFF333333); - labelPaint.setTextSize(26); - labelPaint.setAntiAlias(true); - labelPaint.setTextAlign(Paint.Align.LEFT); - - float labelX = padding + 8; - float labelY = taskY + (taskHeight / 2) + 8; - canvas.drawText(task.getName(), labelX, labelY, labelPaint); } } } From 0ab28713bf00c0211457fc3f7d3a886bfa46bbe4 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 20:52:41 -0700 Subject: [PATCH 10/13] Fix sizing --- .../com/github/mikephil/charting/charts/GanttChart.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index 1e445471c2..50b1ba1fbe 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -75,10 +75,10 @@ protected void onDraw(Canvas canvas) { } private void calculateDimensions() { - chartLeft = padding + 100; // Leave space for task labels - chartTop = padding + 50; + chartLeft = padding + 70; // Smaller left margin + chartTop = padding + 30; chartRight = getWidth() - padding; - chartBottom = getHeight() - padding - 50; + chartBottom = getHeight() - padding - 30; } private void drawGrid(Canvas canvas) { @@ -135,7 +135,7 @@ private void drawTasks(Canvas canvas) { } // Draw task label on left side - float labelX = chartLeft - 15; + float labelX = chartLeft - 20; float labelY = taskY + (taskHeight / 2) + 8; canvas.drawText(task.getName(), labelX, labelY, labelPaint); From 1cf1f1b824b2193ca90bf2a6d75f870b8d4c36b9 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 21:04:14 -0700 Subject: [PATCH 11/13] Add dynamic task sizing --- .../mikephil/charting/charts/GanttChart.java | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index 50b1ba1fbe..22635c4054 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -10,10 +10,6 @@ import com.github.mikephil.charting.data.GanttChartData; import com.github.mikephil.charting.data.GanttTask; -/** - * A custom Gantt chart view that renders tasks as horizontal bars. - * Each bar represents a task with its start time and duration. - */ public class GanttChart extends View { private GanttChartData data; private Paint taskPaint; @@ -24,7 +20,6 @@ public class GanttChart extends View { private float chartTop; private float chartRight; private float chartBottom; - private float taskHeight = 40; private float padding = 16; public GanttChart(Context context) { @@ -75,31 +70,46 @@ protected void onDraw(Canvas canvas) { } private void calculateDimensions() { - chartLeft = padding + 70; // Smaller left margin + chartLeft = padding + 70; chartTop = padding + 30; chartRight = getWidth() - padding; chartBottom = getHeight() - padding - 30; } + // Dynamically calculate task height based on available space + private float getTaskHeight() { + if (data == null || data.getTaskCount() == 0) return 40; + float availableHeight = chartBottom - chartTop; + int taskCount = data.getTaskCount(); + // 80% of slot for bar, 20% for gap + return (availableHeight / taskCount) * 0.8f; + } + + private float getTaskSpacing() { + if (data == null || data.getTaskCount() == 0) return 12; + float availableHeight = chartBottom - chartTop; + int taskCount = data.getTaskCount(); + return (availableHeight / taskCount) * 0.2f; + } + private void drawGrid(Canvas canvas) { - // Draw vertical grid lines float minTime = data.getMinTime(); float maxTime = data.getMaxTime(); float timeRange = maxTime - minTime; if (timeRange == 0) timeRange = 100; int gridLines = 10; + Paint timeLabelPaint = new Paint(); + timeLabelPaint.setColor(0xFF666666); + timeLabelPaint.setTextSize(22); + timeLabelPaint.setAntiAlias(true); + timeLabelPaint.setTextAlign(Paint.Align.CENTER); + for (int i = 0; i <= gridLines; i++) { float x = chartLeft + (i / (float) gridLines) * (chartRight - chartLeft); canvas.drawLine(x, chartTop, x, chartBottom, gridPaint); - // Draw time labels float time = minTime + (i / (float) gridLines) * timeRange; - Paint timeLabelPaint = new Paint(); - timeLabelPaint.setColor(0xFF666666); - timeLabelPaint.setTextSize(22); - timeLabelPaint.setAntiAlias(true); - timeLabelPaint.setTextAlign(Paint.Align.CENTER); canvas.drawText(String.format("%.0f", time), x, chartBottom + 30, timeLabelPaint); } } @@ -110,6 +120,10 @@ private void drawTasks(Canvas canvas) { float timeRange = maxTime - minTime; if (timeRange == 0) timeRange = 100; + float taskHeight = getTaskHeight(); + float taskSpacing = getTaskSpacing(); + float slotHeight = taskHeight + taskSpacing; + Paint labelPaint = new Paint(); labelPaint.setColor(0xFF333333); labelPaint.setTextSize(24); @@ -124,28 +138,22 @@ private void drawTasks(Canvas canvas) { for (int i = 0; i < data.getTaskCount(); i++) { GanttTask task = data.getTask(i); - // Calculate position - float taskY = chartTop + i * (taskHeight + 12); + float taskY = chartTop + i * slotHeight; float startX = chartLeft + ((task.getStartTime() - minTime) / timeRange) * (chartRight - chartLeft); float endX = chartLeft + ((task.getEndTime() - minTime) / timeRange) * (chartRight - chartLeft); - // Ensure minimum width for bars if (endX - startX < 10) { endX = startX + 10; } - // Draw task label on left side - float labelX = chartLeft - 20; + // Center label vertically in the slot float labelY = taskY + (taskHeight / 2) + 8; - canvas.drawText(task.getName(), labelX, labelY, labelPaint); + canvas.drawText(task.getName(), chartLeft - 20, labelY, labelPaint); - // Draw task bar RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); taskPaint.setColor(task.getColor()); canvas.drawRect(rect, taskPaint); - - // Draw border canvas.drawRect(rect, borderPaint); } } -} +} \ No newline at end of file From 3d7c41a5cf9a726bb6091da02b07edeb4cda5e25 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 21:06:00 -0700 Subject: [PATCH 12/13] Add Gantt Chart documentation --- GANTT_CHART.md | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 GANTT_CHART.md diff --git a/GANTT_CHART.md b/GANTT_CHART.md new file mode 100644 index 0000000000..cf6bedda02 --- /dev/null +++ b/GANTT_CHART.md @@ -0,0 +1,145 @@ +# Gantt Chart Feature + +A native Gantt chart implementation for MPAndroidChart that visualizes tasks as horizontal bars positioned by time intervals. + +## Features + +- **Time-based positioning** - Tasks positioned by start time and duration +- **Responsive layout** - Automatically scales to fit any number of tasks +- **Customizable colors** - Each task can have its own color +- **Grid lines** - Time scale visualization with grid lines and labels +- **Clean design** - Professional appearance with task labels and borders + +## Usage + +### 1. Add to your layout XML + +```xml + + + + + + +``` + +### 2. Create your Activity + +```java +package com.example.myapp; + +import android.graphics.Color; +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import com.github.mikephil.charting.charts.GanttChart; +import com.github.mikephil.charting.data.GanttChartData; +import com.github.mikephil.charting.data.GanttTask; + +public class GanttChartActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_gantt_chart); + + GanttChart chart = findViewById(R.id.ganttChart); + + // Create Gantt chart data + GanttChartData ganttData = new GanttChartData(); + + // Add tasks (name, startTime, duration, color) + ganttData.addTask(new GanttTask("Design", 0, 30, Color.rgb(255, 107, 107))); + ganttData.addTask(new GanttTask("Development", 20, 40, Color.rgb(66, 165, 245))); + ganttData.addTask(new GanttTask("Testing", 50, 20, Color.rgb(76, 175, 80))); + ganttData.addTask(new GanttTask("Deployment", 65, 15, Color.rgb(255, 193, 7))); + + // Set data and render + chart.setData(ganttData); + } +} +``` + +## API Reference + +### GanttTask + +Represents a single task in the Gantt chart. + +```java +GanttTask task = new GanttTask( + "Task Name", // String: display name + 0, // float: start time + 50, // float: duration + Color.BLUE // int: task color +); +``` + +**Methods:** +- `getName()` - Get task name +- `getStartTime()` - Get start time +- `getDuration()` - Get task duration +- `getEndTime()` - Get end time (startTime + duration) +- `getColor()` - Get task color + +### GanttChartData + +Container for managing tasks. + +```java +GanttChartData data = new GanttChartData(); +data.addTask(task1); +data.addTask(task2); +data.clearTasks(); +``` + +**Methods:** +- `addTask(GanttTask)` - Add a single task +- `addTasks(List)` - Add multiple tasks +- `getTasks()` - Get all tasks +- `getTaskCount()` - Get number of tasks +- `getMinTime()` - Get earliest start time +- `getMaxTime()` - Get latest end time +- `clearTasks()` - Remove all tasks + +### GanttChart + +Custom View that renders the Gantt chart. + +```java +GanttChart chart = findViewById(R.id.ganttChart); +chart.setData(ganttData); +``` + +**Methods:** +- `setData(GanttChartData)` - Set chart data and trigger redraw + +## Example Use Cases + +- **Project Management** - Visualize project timeline and tasks +- **Resource Allocation** - Show resource usage over time +- **Production Scheduling** - Display manufacturing timeline +- **Event Planning** - Timeline visualization for events + +## Tips + +- Use consistent time units (hours, days, weeks) across all tasks +- Ensure start times and durations use the same scale +- Keep task names reasonably short for better display +- Use contrasting colors for task visibility + +## Customization + +The chart automatically scales to fit your screen size and number of tasks. All styling parameters are calculated dynamically based on: +- Available screen space +- Number of tasks +- Time range of tasks + +For visual customization, modify the `GanttChart` class directly: +- Adjust padding in `calculateDimensions()` +- Change grid line color/style in `drawGrid()` +- Modify task bar appearance in `drawTasks()` From bdf5a7f47e566152f913e7edccf0b8ceea99a9dd Mon Sep 17 00:00:00 2001 From: David Date: Thu, 2 Apr 2026 21:10:50 -0700 Subject: [PATCH 13/13] Reduce bar thickness --- .../com/github/mikephil/charting/charts/GanttChart.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java index 22635c4054..7dfbd5b368 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -81,15 +81,15 @@ private float getTaskHeight() { if (data == null || data.getTaskCount() == 0) return 40; float availableHeight = chartBottom - chartTop; int taskCount = data.getTaskCount(); - // 80% of slot for bar, 20% for gap - return (availableHeight / taskCount) * 0.8f; + // 50% of slot for bar, 50% for gap + return (availableHeight / taskCount) * 0.5f; } private float getTaskSpacing() { if (data == null || data.getTaskCount() == 0) return 12; float availableHeight = chartBottom - chartTop; int taskCount = data.getTaskCount(); - return (availableHeight / taskCount) * 0.2f; + return (availableHeight / taskCount) * 0.5f; } private void drawGrid(Canvas canvas) {