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()` 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 99334e601a..0443ce2f02 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..beb0e7e551 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/chartdemo/TimeIntervalChartActivity.java @@ -0,0 +1,39 @@ +package com.xxmassdeveloper.chartdemo; + +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; +import com.xxmassdeveloper.mpchartexample.R; + +/** + * Demo activity showing Gantt-style timeline visualization. + * Each horizontal bar represents a task 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); + + GanttChart chart = findViewById(R.id.chart); + + // Create Gantt chart data + GanttChartData ganttData = new GanttChartData(); + + // 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 + + // Set data and render + chart.setData(ganttData); + } +} + 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..ae20c37731 --- /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..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 @@ -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,12 +53,12 @@ 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++) { float value = vals[k]; + float yStart; if (value >= 0f) { y = posY; @@ -71,7 +72,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 >= yStart ? y : yStart; right = y <= yStart ? y : yStart; 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..7dfbd5b368 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/GanttChart.java @@ -0,0 +1,159 @@ +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; + +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 padding = 16; + + 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(0xFFCCCCCC); + gridPaint.setStrokeWidth(1); + + textPaint = new Paint(); + textPaint.setColor(0xFF666666); + textPaint.setTextSize(28); + 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 + 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(); + // 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.5f; + } + + private void drawGrid(Canvas canvas) { + 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); + + float time = minTime + (i / (float) gridLines) * timeRange; + canvas.drawText(String.format("%.0f", time), x, chartBottom + 30, timeLabelPaint); + } + } + + private void drawTasks(Canvas canvas) { + float minTime = data.getMinTime(); + float maxTime = data.getMaxTime(); + 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); + 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); + + float taskY = chartTop + i * slotHeight; + float startX = chartLeft + ((task.getStartTime() - minTime) / timeRange) * (chartRight - chartLeft); + float endX = chartLeft + ((task.getEndTime() - minTime) / timeRange) * (chartRight - chartLeft); + + if (endX - startX < 10) { + endX = startX + 10; + } + + // Center label vertically in the slot + float labelY = taskY + (taskHeight / 2) + 8; + canvas.drawText(task.getName(), chartLeft - 20, labelY, labelPaint); + + RectF rect = new RectF(startX, taskY, endX, taskY + taskHeight); + taskPaint.setColor(task.getColor()); + canvas.drawRect(rect, taskPaint); + canvas.drawRect(rect, borderPaint); + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..62d5a91d55 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/GanttUtils.java @@ -0,0 +1,73 @@ +package com.github.mikephil.charting.utils; + +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 Gantt chart data and tasks. + * + *

Usage Example:

+ *
+ * // Create Gantt chart data
+ * GanttChartData ganttData = new GanttChartData();
+ * 
+ * // 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)));
+ * 
+ * // Or use the helper
+ * GanttChartData data = GanttUtils.createSampleGanttData();
+ * 
+ */ +public class GanttUtils { + + private GanttUtils() { + // utility class - no instances + } + + /** + * 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 GanttChartData createGanttData() { + return new GanttChartData(); + } + + /** + * 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 GanttTask createTask(String name, float startTime, float duration, int color) { + return new GanttTask(name, startTime, duration, color); + } +} 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