After Width: | Height: | Size: 40 KiB |
@ -0,0 +1,252 @@
|
|||||||
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.support.v4.view.MotionEventCompat;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.isoron.helpers.ColorHelper;
|
||||||
|
import org.isoron.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.Score;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HabitScoreView extends View
|
||||||
|
{
|
||||||
|
public static final int BUCKET_SIZE = 7;
|
||||||
|
|
||||||
|
private final Paint pGrid;
|
||||||
|
private final float em;
|
||||||
|
private Habit habit;
|
||||||
|
private int columnWidth, columnHeight, nColumns;
|
||||||
|
|
||||||
|
private Paint pText, pGraph;
|
||||||
|
private int dataOffset;
|
||||||
|
|
||||||
|
private int barHeaderHeight;
|
||||||
|
|
||||||
|
private int[] colors;
|
||||||
|
private float prevX;
|
||||||
|
private float prevY;
|
||||||
|
private List<Score> scores;
|
||||||
|
|
||||||
|
public HabitScoreView(Context context, Habit habit, int columnWidth)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
this.habit = habit;
|
||||||
|
this.columnWidth = columnWidth;
|
||||||
|
|
||||||
|
pText = new Paint();
|
||||||
|
pText.setColor(Color.LTGRAY);
|
||||||
|
pText.setTextAlign(Paint.Align.LEFT);
|
||||||
|
pText.setTextSize(columnWidth * 0.5f);
|
||||||
|
pText.setAntiAlias(true);
|
||||||
|
|
||||||
|
pGraph = new Paint();
|
||||||
|
pGraph.setTextAlign(Paint.Align.CENTER);
|
||||||
|
pGraph.setTextSize(columnWidth * 0.5f);
|
||||||
|
pGraph.setAntiAlias(true);
|
||||||
|
pGraph.setStrokeWidth(columnWidth * 0.1f);
|
||||||
|
|
||||||
|
pGrid = new Paint();
|
||||||
|
pGrid.setColor(Color.LTGRAY);
|
||||||
|
pGrid.setAntiAlias(true);
|
||||||
|
pGrid.setStrokeWidth(columnWidth * 0.05f);
|
||||||
|
|
||||||
|
columnHeight = 8 * columnWidth;
|
||||||
|
barHeaderHeight = columnWidth;
|
||||||
|
em = pText.getFontSpacing();
|
||||||
|
|
||||||
|
colors = new int[4];
|
||||||
|
|
||||||
|
colors[0] = Color.rgb(230, 230, 230);
|
||||||
|
colors[3] = habit.color;
|
||||||
|
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
||||||
|
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchScores()
|
||||||
|
{
|
||||||
|
|
||||||
|
long toTimestamp = DateHelper.getStartOfToday();
|
||||||
|
for (int i = 0; i < dataOffset * BUCKET_SIZE; i++)
|
||||||
|
toTimestamp -= DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
|
long fromTimestamp = toTimestamp;
|
||||||
|
for (int i = 0; i < nColumns * BUCKET_SIZE; i++)
|
||||||
|
fromTimestamp -= DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
|
scores = habit.getScores(fromTimestamp, toTimestamp, BUCKET_SIZE * DateHelper.millisecondsInOneDay,
|
||||||
|
toTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
setMeasuredDimension(getMeasuredWidth(), columnHeight + 2 * barHeaderHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||||
|
{
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
nColumns = w / columnWidth;
|
||||||
|
fetchScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
float lineHeight = pText.getFontSpacing();
|
||||||
|
float barHeaderOffset = lineHeight * 0.4f;
|
||||||
|
|
||||||
|
RectF rGrid = new RectF(0, 0, nColumns * columnWidth, columnHeight);
|
||||||
|
rGrid.offset(0, barHeaderHeight);
|
||||||
|
drawGrid(canvas, rGrid);
|
||||||
|
|
||||||
|
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
||||||
|
SimpleDateFormat dfDay = new SimpleDateFormat("d");
|
||||||
|
|
||||||
|
String previousMonth = "";
|
||||||
|
|
||||||
|
pGraph.setColor(habit.color);
|
||||||
|
RectF prevR = null;
|
||||||
|
|
||||||
|
for (int offset = nColumns - scores.size(); offset < nColumns; offset++)
|
||||||
|
{
|
||||||
|
Score score = scores.get(offset - nColumns + scores.size());
|
||||||
|
String month = dfMonth.format(score.timestamp);
|
||||||
|
String day = dfDay.format(score.timestamp);
|
||||||
|
|
||||||
|
long s = score.score;
|
||||||
|
double sRelative = ((double) s) / Habit.MAX_SCORE;
|
||||||
|
|
||||||
|
int height = (int) (columnHeight * sRelative);
|
||||||
|
|
||||||
|
RectF r = new RectF(0, 0, columnWidth, columnWidth);
|
||||||
|
r.offset(offset * columnWidth,
|
||||||
|
barHeaderHeight + columnHeight - height - columnWidth / 2);
|
||||||
|
|
||||||
|
if (prevR != null)
|
||||||
|
{
|
||||||
|
drawLine(canvas, prevR, r);
|
||||||
|
drawMarker(canvas, prevR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset == nColumns - 1) drawMarker(canvas, r);
|
||||||
|
|
||||||
|
prevR = r;
|
||||||
|
|
||||||
|
r = new RectF(0, 0, columnWidth, columnHeight);
|
||||||
|
r.offset(offset * columnWidth, barHeaderHeight);
|
||||||
|
if (!month.equals(previousMonth))
|
||||||
|
{
|
||||||
|
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousMonth = month;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawGrid(Canvas canvas, RectF rGrid)
|
||||||
|
{
|
||||||
|
// pGrid.setColor(Color.rgb(230, 230, 230));
|
||||||
|
// pGrid.setStyle(Paint.Style.STROKE);
|
||||||
|
// canvas.drawRect(rGrid, pGrid);
|
||||||
|
|
||||||
|
int nRows = 5;
|
||||||
|
float rowHeight = rGrid.height() / nRows;
|
||||||
|
|
||||||
|
pGrid.setColor(Color.rgb(240, 240, 240));
|
||||||
|
for (int i = 0; i < nRows; i++)
|
||||||
|
{
|
||||||
|
canvas.drawText(String.format("%d%%", (100 - i * 100 / nRows)), rGrid.left + 0.5f * em,
|
||||||
|
rGrid.top + 1f * em, pText);
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
|
||||||
|
rGrid.offset(0, rowHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawLine(Canvas canvas, RectF rectFrom, RectF rectTo)
|
||||||
|
{
|
||||||
|
pGraph.setColor(habit.color);
|
||||||
|
canvas.drawLine(rectFrom.centerX(), rectFrom.centerY(), rectTo.centerX(), rectTo.centerY(),
|
||||||
|
pGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawMarker(Canvas canvas, RectF rect)
|
||||||
|
{
|
||||||
|
rect.inset(columnWidth * 0.15f, columnWidth * 0.15f);
|
||||||
|
pGraph.setColor(Color.WHITE);
|
||||||
|
canvas.drawOval(rect, pGraph);
|
||||||
|
|
||||||
|
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
|
||||||
|
pGraph.setColor(habit.color);
|
||||||
|
canvas.drawOval(rect, pGraph);
|
||||||
|
|
||||||
|
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
|
||||||
|
pGraph.setColor(Color.WHITE);
|
||||||
|
canvas.drawOval(rect, pGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent event)
|
||||||
|
{
|
||||||
|
int action = event.getAction();
|
||||||
|
|
||||||
|
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
||||||
|
final float x = MotionEventCompat.getX(event, pointerIndex);
|
||||||
|
final float y = MotionEventCompat.getY(event, pointerIndex);
|
||||||
|
|
||||||
|
if (action == MotionEvent.ACTION_DOWN)
|
||||||
|
{
|
||||||
|
prevX = x;
|
||||||
|
prevY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == MotionEvent.ACTION_MOVE)
|
||||||
|
{
|
||||||
|
float dx = x - prevX;
|
||||||
|
float dy = y - prevY;
|
||||||
|
|
||||||
|
if (Math.abs(dy) > Math.abs(dx)) return false;
|
||||||
|
getParent().requestDisallowInterceptTouchEvent(true);
|
||||||
|
if (move(dx))
|
||||||
|
{
|
||||||
|
prevX = x;
|
||||||
|
prevY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean move(float dx)
|
||||||
|
{
|
||||||
|
int newDataOffset = dataOffset + (int) (dx / columnWidth);
|
||||||
|
newDataOffset = Math.max(0, newDataOffset);
|
||||||
|
|
||||||
|
if (newDataOffset != dataOffset)
|
||||||
|
{
|
||||||
|
dataOffset = newDataOffset;
|
||||||
|
fetchScores();
|
||||||
|
invalidate();
|
||||||
|
return true;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 11 KiB |
@ -1,20 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="habitsListHeaderStyle">
|
<style name="habitsListHeaderStyle" parent="habitsListHeaderBasicStyle">
|
||||||
<item name="android:layout_width">match_parent</item>
|
|
||||||
<item name="android:layout_height">wrap_content</item>
|
|
||||||
<item name="android:layout_alignParentTop">true</item>
|
|
||||||
<item name="android:background">#f0f0f0</item>
|
<item name="android:background">#f0f0f0</item>
|
||||||
<item name="android:elevation">2dp</item>
|
<item name="android:elevation">2dp</item>
|
||||||
<item name="android:paddingRight">4dp</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="habitsListCheckStyle">
|
<style name="habitsListCheckStyle" parent="habitsListCheckBasicStyle">
|
||||||
<item name="android:focusable">false</item>
|
|
||||||
<item name="android:minHeight">42dp</item>
|
|
||||||
<item name="android:minWidth">42dp</item>
|
|
||||||
<item name="android:gravity">center</item>
|
|
||||||
<item name="android:background">@drawable/ripple_background</item>
|
<item name="android:background">@drawable/ripple_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="DragSortListView">
|
||||||
|
<attr name="collapsed_height" format="dimension"/>
|
||||||
|
<attr name="drag_scroll_start" format="float"/>
|
||||||
|
<attr name="max_drag_scroll_speed" format="float"/>
|
||||||
|
<attr name="float_background_color" format="color"/>
|
||||||
|
<attr name="remove_mode">
|
||||||
|
<enum name="clickRemove" value="0"/>
|
||||||
|
<enum name="flingRemove" value="1"/>
|
||||||
|
</attr>
|
||||||
|
<attr name="track_drag_sort" format="boolean"/>
|
||||||
|
<attr name="float_alpha" format="float"/>
|
||||||
|
<attr name="slide_shuffle_speed" format="float"/>
|
||||||
|
<attr name="remove_animation_duration" format="integer"/>
|
||||||
|
<attr name="drop_animation_duration" format="integer"/>
|
||||||
|
<attr name="drag_enabled" format="boolean"/>
|
||||||
|
<attr name="sort_enabled" format="boolean"/>
|
||||||
|
<attr name="remove_enabled" format="boolean"/>
|
||||||
|
<attr name="drag_start_mode">
|
||||||
|
<enum name="onDown" value="0"/>
|
||||||
|
<enum name="onMove" value="1"/>
|
||||||
|
<enum name="onLongPress" value="2"/>
|
||||||
|
</attr>
|
||||||
|
<attr name="drag_handle_id" format="integer"/>
|
||||||
|
<attr name="fling_handle_id" format="integer"/>
|
||||||
|
<attr name="click_remove_id" format="integer"/>
|
||||||
|
<attr name="use_default_controller" format="boolean"/>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
@ -1,54 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
|
||||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="small_square_size">20dp</dimen>
|
||||||
<dimen name="square_size">20dp</dimen>
|
<dimen name="check_square_size">42dp</dimen>
|
||||||
|
|
||||||
<!-- Color picker -->
|
|
||||||
<dimen name="color_swatch_large">64dip</dimen>
|
|
||||||
<dimen name="color_swatch_small">48dip</dimen>
|
|
||||||
<dimen name="color_swatch_margins_large">8dip</dimen>
|
|
||||||
<dimen name="color_swatch_margins_small">4dip</dimen>
|
|
||||||
|
|
||||||
<!-- Date and time picker -->
|
|
||||||
<item name="circle_radius_multiplier" format="float" type="string">0.82</item>
|
|
||||||
<item name="circle_radius_multiplier_24HourMode" format="float" type="string">0.85</item>
|
|
||||||
<item name="selection_radius_multiplier" format="float" type="string">0.16</item>
|
|
||||||
<item name="ampm_circle_radius_multiplier" format="float" type="string">0.19</item>
|
|
||||||
<item name="numbers_radius_multiplier_normal" format="float" type="string">0.81</item>
|
|
||||||
<item name="numbers_radius_multiplier_inner" format="float" type="string">0.60</item>
|
|
||||||
<item name="numbers_radius_multiplier_outer" format="float" type="string">0.83</item>
|
|
||||||
<item name="text_size_multiplier_normal" format="float" type="string">0.17</item>
|
|
||||||
<item name="text_size_multiplier_inner" format="float" type="string">0.14</item>
|
|
||||||
<item name="text_size_multiplier_outer" format="float" type="string">0.11</item>
|
|
||||||
<dimen name="time_label_size">60sp</dimen>
|
|
||||||
<dimen name="extra_time_label_margin">-30dp</dimen>
|
|
||||||
<dimen name="ampm_label_size">16sp</dimen>
|
|
||||||
<dimen name="done_label_size">14sp</dimen>
|
|
||||||
<dimen name="ampm_left_padding">6dip</dimen>
|
|
||||||
<dimen name="separator_padding">4dip</dimen>
|
|
||||||
<dimen name="header_height">96dip</dimen>
|
|
||||||
<dimen name="footer_height">48dip</dimen>
|
|
||||||
<dimen name="minimum_margin_sides">48dip</dimen>
|
|
||||||
<dimen name="minimum_margin_top_bottom">24dip</dimen>
|
|
||||||
<dimen name="picker_dimen">270dip</dimen>
|
|
||||||
<dimen name="date_picker_component_width">270dp</dimen>
|
|
||||||
<dimen name="date_picker_header_height">30dp</dimen>
|
|
||||||
<dimen name="selected_calendar_layout_height">155dp</dimen>
|
|
||||||
<dimen name="date_picker_view_animator_height">270dp</dimen>
|
|
||||||
<dimen name="done_button_height">42dp</dimen>
|
|
||||||
<dimen name="month_list_item_header_height">50dp</dimen>
|
|
||||||
<dimen name="month_day_label_text_size">10sp</dimen>
|
|
||||||
<dimen name="day_number_select_circle_radius">16dp</dimen>
|
|
||||||
<dimen name="month_select_circle_radius">45dp</dimen>
|
|
||||||
<dimen name="selected_date_year_size">30dp</dimen>
|
|
||||||
<dimen name="selected_date_day_size">75dp</dimen>
|
|
||||||
<dimen name="selected_date_month_size">30dp</dimen>
|
|
||||||
<dimen name="date_picker_header_text_size">14dp</dimen>
|
|
||||||
<dimen name="month_label_size">16sp</dimen>
|
|
||||||
<dimen name="day_number_size">16sp</dimen>
|
|
||||||
<dimen name="year_label_height">64dp</dimen>
|
|
||||||
<dimen name="year_label_text_size">22dp</dimen>
|
|
||||||
</resources>
|
</resources>
|
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="color_swatch_large">64dip</dimen>
|
||||||
|
<dimen name="color_swatch_small">48dip</dimen>
|
||||||
|
<dimen name="color_swatch_margins_large">8dip</dimen>
|
||||||
|
<dimen name="color_swatch_margins_small">4dip</dimen>
|
||||||
|
</resources>
|
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<item name="circle_radius_multiplier" format="float" type="string">0.82</item>
|
||||||
|
<item name="circle_radius_multiplier_24HourMode" format="float" type="string">0.85</item>
|
||||||
|
<item name="selection_radius_multiplier" format="float" type="string">0.16</item>
|
||||||
|
<item name="ampm_circle_radius_multiplier" format="float" type="string">0.19</item>
|
||||||
|
<item name="numbers_radius_multiplier_normal" format="float" type="string">0.81</item>
|
||||||
|
<item name="numbers_radius_multiplier_inner" format="float" type="string">0.60</item>
|
||||||
|
<item name="numbers_radius_multiplier_outer" format="float" type="string">0.83</item>
|
||||||
|
<item name="text_size_multiplier_normal" format="float" type="string">0.17</item>
|
||||||
|
<item name="text_size_multiplier_inner" format="float" type="string">0.14</item>
|
||||||
|
<item name="text_size_multiplier_outer" format="float" type="string">0.11</item>
|
||||||
|
|
||||||
|
<dimen name="time_label_size">60sp</dimen>
|
||||||
|
<dimen name="extra_time_label_margin">-30dp</dimen>
|
||||||
|
<dimen name="ampm_label_size">16sp</dimen>
|
||||||
|
<dimen name="done_label_size">14sp</dimen>
|
||||||
|
<dimen name="ampm_left_padding">6dip</dimen>
|
||||||
|
<dimen name="separator_padding">4dip</dimen>
|
||||||
|
<dimen name="header_height">96dip</dimen>
|
||||||
|
<dimen name="footer_height">48dip</dimen>
|
||||||
|
<dimen name="minimum_margin_sides">48dip</dimen>
|
||||||
|
<dimen name="minimum_margin_top_bottom">24dip</dimen>
|
||||||
|
<dimen name="picker_dimen">270dip</dimen>
|
||||||
|
<dimen name="date_picker_component_width">270dp</dimen>
|
||||||
|
<dimen name="date_picker_header_height">30dp</dimen>
|
||||||
|
<dimen name="selected_calendar_layout_height">155dp</dimen>
|
||||||
|
<dimen name="date_picker_view_animator_height">270dp</dimen>
|
||||||
|
<dimen name="done_button_height">42dp</dimen>
|
||||||
|
<dimen name="month_list_item_header_height">50dp</dimen>
|
||||||
|
<dimen name="month_day_label_text_size">10sp</dimen>
|
||||||
|
<dimen name="day_number_select_circle_radius">16dp</dimen>
|
||||||
|
<dimen name="month_select_circle_radius">45dp</dimen>
|
||||||
|
<dimen name="selected_date_year_size">30dp</dimen>
|
||||||
|
<dimen name="selected_date_day_size">75dp</dimen>
|
||||||
|
<dimen name="selected_date_month_size">30dp</dimen>
|
||||||
|
<dimen name="date_picker_header_text_size">14dp</dimen>
|
||||||
|
<dimen name="month_label_size">16sp</dimen>
|
||||||
|
<dimen name="day_number_size">16sp</dimen>
|
||||||
|
<dimen name="year_label_height">64dp</dimen>
|
||||||
|
<dimen name="year_label_text_size">22dp</dimen>
|
||||||
|
</resources>
|