mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Refactor custom views
This commit is contained in:
@@ -22,52 +22,93 @@ import android.graphics.Color;
|
|||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Paint.Align;
|
import android.graphics.Paint.Align;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.v4.view.MotionEventCompat;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.isoron.helpers.ColorHelper;
|
import org.isoron.helpers.ColorHelper;
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
public class HabitHistoryView extends View
|
public class HabitHistoryView extends ScrollableDataView
|
||||||
{
|
{
|
||||||
|
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
private int[] checks;
|
private int[] checkmarks;
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private Paint pSquareBg, pSquareFg, pTextHeader;
|
private Paint pSquareBg, pSquareFg, pTextHeader;
|
||||||
|
private int squareSpacing;
|
||||||
|
|
||||||
private int squareSize, squareSpacing;
|
private float squareTextOffset;
|
||||||
private int nColumns, offsetWeeks;
|
private float headerTextOffset;
|
||||||
|
|
||||||
private int colorPrimary, colorPrimaryBrighter, grey;
|
|
||||||
private Float prevX, prevY;
|
|
||||||
|
|
||||||
private String wdays[];
|
private String wdays[];
|
||||||
|
private SimpleDateFormat dfMonth;
|
||||||
|
private SimpleDateFormat dfYear;
|
||||||
|
|
||||||
public HabitHistoryView(Context context, Habit habit, int squareSize)
|
private Calendar baseDate;
|
||||||
|
private int nDays;
|
||||||
|
private int todayWeekday;
|
||||||
|
private int colors[];
|
||||||
|
|
||||||
|
public HabitHistoryView(Context context, Habit habit, int baseSize)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
this.context = context;
|
|
||||||
this.squareSize = squareSize;
|
|
||||||
|
|
||||||
colorPrimary = habit.color;
|
setDimensions(baseSize);
|
||||||
colorPrimaryBrighter = ColorHelper.mixColors(colorPrimary, Color.WHITE, 0.5f);
|
createPaints();
|
||||||
grey = Color.rgb(230, 230, 230);
|
createColors();
|
||||||
|
|
||||||
|
wdays = DateHelper.getShortDayNames();
|
||||||
|
dfMonth = new SimpleDateFormat("MMM");
|
||||||
|
dfYear = new SimpleDateFormat("yyyy");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDate()
|
||||||
|
{
|
||||||
|
baseDate = new GregorianCalendar();
|
||||||
|
baseDate.add(Calendar.DAY_OF_YEAR, -(dataOffset - 1) * 7);
|
||||||
|
|
||||||
|
nDays = nColumns * 7;
|
||||||
|
todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
|
||||||
|
|
||||||
|
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
||||||
|
baseDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||||
|
{
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
updateDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createColors()
|
||||||
|
{
|
||||||
|
int primaryColor = habit.color;
|
||||||
|
int primaryColorBright = ColorHelper.mixColors(primaryColor, Color.WHITE, 0.5f);
|
||||||
|
int grey = Color.rgb(230, 230, 230);
|
||||||
|
|
||||||
|
colors = new int[3];
|
||||||
|
colors[0] = grey;
|
||||||
|
colors[1] = primaryColorBright;
|
||||||
|
colors[2] = primaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDimensions(int baseSize)
|
||||||
|
{
|
||||||
|
columnWidth = baseSize;
|
||||||
|
columnHeight = 8 * baseSize;
|
||||||
squareSpacing = 2;
|
squareSpacing = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPaints()
|
||||||
|
{
|
||||||
pTextHeader = new Paint();
|
pTextHeader = new Paint();
|
||||||
pTextHeader.setColor(Color.LTGRAY);
|
pTextHeader.setColor(Color.LTGRAY);
|
||||||
pTextHeader.setTextAlign(Align.LEFT);
|
pTextHeader.setTextAlign(Align.LEFT);
|
||||||
pTextHeader.setTextSize(squareSize * 0.5f);
|
pTextHeader.setTextSize(columnWidth * 0.5f);
|
||||||
pTextHeader.setAntiAlias(true);
|
pTextHeader.setAntiAlias(true);
|
||||||
|
|
||||||
pSquareBg = new Paint();
|
pSquareBg = new Paint();
|
||||||
@@ -76,179 +117,123 @@ public class HabitHistoryView extends View
|
|||||||
pSquareFg = new Paint();
|
pSquareFg = new Paint();
|
||||||
pSquareFg.setColor(Color.WHITE);
|
pSquareFg.setColor(Color.WHITE);
|
||||||
pSquareFg.setAntiAlias(true);
|
pSquareFg.setAntiAlias(true);
|
||||||
pSquareFg.setTextSize(squareSize * 0.5f);
|
pSquareFg.setTextSize(columnWidth * 0.5f);
|
||||||
pSquareFg.setTextAlign(Align.CENTER);
|
pSquareFg.setTextAlign(Align.CENTER);
|
||||||
|
|
||||||
wdays = DateHelper.getShortDayNames();
|
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
|
||||||
|
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected void fetchData()
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
|
||||||
{
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
setMeasuredDimension(getMeasuredWidth(), 8 * squareSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
|
||||||
{
|
|
||||||
nColumns = (w / squareSize) - 1;
|
|
||||||
fetchReps();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetchReps()
|
|
||||||
{
|
{
|
||||||
Calendar currentDate = new GregorianCalendar();
|
Calendar currentDate = new GregorianCalendar();
|
||||||
currentDate.add(Calendar.DAY_OF_YEAR, -offsetWeeks * 7);
|
currentDate.add(Calendar.DAY_OF_YEAR, -dataOffset * 7);
|
||||||
int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7;
|
int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7;
|
||||||
|
|
||||||
long dateTo = DateHelper.getStartOfToday();
|
long dateTo = DateHelper.getStartOfToday();
|
||||||
for (int i = 0; i < 7 - dayOfWeek; i++)
|
for (int i = 0; i < 7 - dayOfWeek; i++)
|
||||||
dateTo += DateHelper.millisecondsInOneDay;
|
dateTo += DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
for (int i = 0; i < offsetWeeks * 7; i++)
|
for (int i = 0; i < dataOffset * 7; i++)
|
||||||
dateTo -= DateHelper.millisecondsInOneDay;
|
dateTo -= DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
long dateFrom = dateTo;
|
long dateFrom = dateTo;
|
||||||
for (int i = 0; i < nColumns * 7; i++)
|
for (int i = 0; i < nColumns * 7; i++)
|
||||||
dateFrom -= DateHelper.millisecondsInOneDay;
|
dateFrom -= DateHelper.millisecondsInOneDay;
|
||||||
|
|
||||||
checks = habit.getCheckmarks(dateFrom, dateTo);
|
checkmarks = habit.getCheckmarks(dateFrom, dateTo);
|
||||||
|
updateDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String previousMonth;
|
||||||
|
private String previousYear;
|
||||||
|
private boolean justPrintedYear;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
{
|
{
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
|
|
||||||
Rect square = new Rect(0, 0, squareSize - squareSpacing, squareSize - squareSpacing);
|
Rect location = new Rect(0, 0, columnWidth - squareSpacing, columnWidth - squareSpacing);
|
||||||
|
|
||||||
Calendar currentDate = new GregorianCalendar();
|
previousMonth = "";
|
||||||
currentDate.add(Calendar.DAY_OF_YEAR, -(offsetWeeks - 1) * 7);
|
previousYear = "";
|
||||||
|
justPrintedYear = false;
|
||||||
|
|
||||||
int nDays = nColumns * 7;
|
GregorianCalendar currentDate = (GregorianCalendar) baseDate.clone();
|
||||||
int todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
|
|
||||||
|
|
||||||
currentDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
for (int column = 0; column < nColumns - 1; column++)
|
||||||
currentDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
|
|
||||||
|
|
||||||
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
|
||||||
SimpleDateFormat dfYear = new SimpleDateFormat("yyyy");
|
|
||||||
|
|
||||||
String previousMonth = "";
|
|
||||||
String previousYear = "";
|
|
||||||
|
|
||||||
int colors[] = {grey, colorPrimaryBrighter, colorPrimary};
|
|
||||||
String markers[] =
|
|
||||||
{context.getString(R.string.fa_times), context.getString(R.string.fa_check),
|
|
||||||
context.getString(R.string.fa_check)};
|
|
||||||
|
|
||||||
float squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
|
|
||||||
float headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
|
|
||||||
boolean justPrintedYear = false;
|
|
||||||
|
|
||||||
int k = nDays;
|
|
||||||
for (int i = 0; i < nColumns; i++)
|
|
||||||
{
|
{
|
||||||
String month = dfMonth.format(currentDate.getTime());
|
drawColumn(canvas, location, currentDate, column);
|
||||||
String year = dfYear.format(currentDate.getTime());
|
location.offset(columnWidth, -columnHeight);
|
||||||
|
|
||||||
if (!month.equals(previousMonth))
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
if (justPrintedYear) offset += squareSize;
|
|
||||||
|
|
||||||
canvas.drawText(month, square.left + offset, square.bottom - headerTextOffset,
|
|
||||||
pTextHeader);
|
|
||||||
previousMonth = month;
|
|
||||||
justPrintedYear = false;
|
|
||||||
}
|
|
||||||
else if (!year.equals(previousYear))
|
|
||||||
{
|
|
||||||
canvas.drawText(year, square.left, square.bottom - headerTextOffset, pTextHeader);
|
|
||||||
previousYear = year;
|
|
||||||
justPrintedYear = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
justPrintedYear = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
square.offset(0, squareSize);
|
|
||||||
|
|
||||||
for (int j = 0; j < 7; j++)
|
|
||||||
{
|
|
||||||
if (!(i == nColumns - 1 && offsetWeeks == 0 && j > todayWeekday))
|
|
||||||
{
|
|
||||||
if (k >= checks.length) pSquareBg.setColor(colors[0]);
|
|
||||||
else pSquareBg.setColor(colors[checks[k]]);
|
|
||||||
|
|
||||||
canvas.drawRect(square, pSquareBg);
|
|
||||||
canvas.drawText(Integer.toString(currentDate.get(Calendar.DAY_OF_MONTH)),
|
|
||||||
square.centerX(), square.centerY() + squareTextOffset, pSquareFg);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDate.add(Calendar.DAY_OF_MONTH, 1);
|
|
||||||
square.offset(0, squareSize);
|
|
||||||
k--;
|
|
||||||
}
|
|
||||||
|
|
||||||
square.offset(squareSize, -8 * squareSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawAxis(canvas, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawColumn(Canvas canvas, Rect location, GregorianCalendar date, int column)
|
||||||
|
{
|
||||||
|
drawColumnHeader(canvas, location, date);
|
||||||
|
location.offset(0, columnWidth);
|
||||||
|
|
||||||
|
for (int j = 0; j < 7; j++)
|
||||||
|
{
|
||||||
|
if (!(column == nColumns - 2 && dataOffset == 0 && j > todayWeekday))
|
||||||
|
{
|
||||||
|
int checkmarkOffset = nDays - 7 * column - j;
|
||||||
|
drawSquare(canvas, location, date, checkmarkOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
date.add(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
location.offset(0, columnWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawSquare(Canvas canvas, Rect location, GregorianCalendar date,
|
||||||
|
int checkmarkOffset)
|
||||||
|
{
|
||||||
|
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
|
||||||
|
else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]);
|
||||||
|
|
||||||
|
canvas.drawRect(location, pSquareBg);
|
||||||
|
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
|
||||||
|
canvas.drawText(text, location.centerX(), location.centerY() + squareTextOffset, pSquareFg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawAxis(Canvas canvas, Rect location)
|
||||||
|
{
|
||||||
for (int i = 0; i < 7; i++)
|
for (int i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
square.offset(0, squareSize);
|
location.offset(0, columnWidth);
|
||||||
canvas.drawText(wdays[i], square.left + headerTextOffset,
|
canvas.drawText(wdays[i], location.left + headerTextOffset,
|
||||||
square.bottom - headerTextOffset, pTextHeader);
|
location.bottom - headerTextOffset, pTextHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void drawColumnHeader(Canvas canvas, Rect location, GregorianCalendar date)
|
||||||
public boolean onTouchEvent(MotionEvent event)
|
|
||||||
{
|
{
|
||||||
int action = event.getAction();
|
String month = dfMonth.format(date.getTime());
|
||||||
|
String year = dfYear.format(date.getTime());
|
||||||
|
|
||||||
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
if (!month.equals(previousMonth))
|
||||||
final float x = MotionEventCompat.getX(event, pointerIndex);
|
|
||||||
final float y = MotionEventCompat.getY(event, pointerIndex);
|
|
||||||
|
|
||||||
if (action == MotionEvent.ACTION_DOWN)
|
|
||||||
{
|
{
|
||||||
prevX = x;
|
int offset = 0;
|
||||||
prevY = y;
|
if (justPrintedYear) offset += columnWidth;
|
||||||
}
|
|
||||||
|
|
||||||
if (action == MotionEvent.ACTION_MOVE)
|
canvas.drawText(month, location.left + offset, location.bottom - headerTextOffset,
|
||||||
|
pTextHeader);
|
||||||
|
previousMonth = month;
|
||||||
|
justPrintedYear = false;
|
||||||
|
}
|
||||||
|
else if (!year.equals(previousYear))
|
||||||
{
|
{
|
||||||
float dx = x - prevX;
|
canvas.drawText(year, location.left, location.bottom - headerTextOffset, pTextHeader);
|
||||||
float dy = y - prevY;
|
previousYear = year;
|
||||||
|
justPrintedYear = true;
|
||||||
if (Math.abs(dy) > Math.abs(dx)) return false;
|
|
||||||
getParent().requestDisallowInterceptTouchEvent(true);
|
|
||||||
if (move(dx))
|
|
||||||
{
|
|
||||||
prevX = x;
|
|
||||||
prevY = y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean move(float dx)
|
|
||||||
{
|
|
||||||
int newOffsetWeeks = offsetWeeks + (int) (dx / squareSize);
|
|
||||||
newOffsetWeeks = Math.max(0, newOffsetWeeks);
|
|
||||||
|
|
||||||
if (newOffsetWeeks != offsetWeeks)
|
|
||||||
{
|
{
|
||||||
offsetWeeks = newOffsetWeeks;
|
justPrintedYear = false;
|
||||||
fetchReps();
|
|
||||||
invalidate();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.RectF;
|
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.ColorHelper;
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.helpers.DateHelper;
|
||||||
@@ -33,30 +30,23 @@ import org.isoron.uhabits.models.Score;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HabitScoreView extends View
|
public class HabitScoreView extends ScrollableDataView
|
||||||
{
|
{
|
||||||
public static final int BUCKET_SIZE = 7;
|
public static final int BUCKET_SIZE = 7;
|
||||||
|
|
||||||
private final Paint pGrid;
|
private final Paint pGrid;
|
||||||
private final float em;
|
private final float em;
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
private int columnWidth, columnHeight, nColumns;
|
|
||||||
|
|
||||||
private Paint pText, pGraph;
|
private Paint pText, pGraph;
|
||||||
private int dataOffset;
|
|
||||||
|
|
||||||
private int barHeaderHeight;
|
|
||||||
|
|
||||||
private int[] colors;
|
private int[] colors;
|
||||||
private float prevX;
|
|
||||||
private float prevY;
|
|
||||||
private List<Score> scores;
|
private List<Score> scores;
|
||||||
|
|
||||||
public HabitScoreView(Context context, Habit habit, int columnWidth)
|
public HabitScoreView(Context context, Habit habit, int columnWidth)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
this.columnWidth = columnWidth;
|
|
||||||
|
|
||||||
pText = new Paint();
|
pText = new Paint();
|
||||||
pText.setColor(Color.LTGRAY);
|
pText.setColor(Color.LTGRAY);
|
||||||
@@ -75,8 +65,11 @@ public class HabitScoreView extends View
|
|||||||
pGrid.setAntiAlias(true);
|
pGrid.setAntiAlias(true);
|
||||||
pGrid.setStrokeWidth(columnWidth * 0.05f);
|
pGrid.setStrokeWidth(columnWidth * 0.05f);
|
||||||
|
|
||||||
|
this.columnWidth = columnWidth;
|
||||||
columnHeight = 8 * columnWidth;
|
columnHeight = 8 * columnWidth;
|
||||||
barHeaderHeight = columnWidth;
|
headerHeight = columnWidth;
|
||||||
|
footerHeight = columnWidth;
|
||||||
|
|
||||||
em = pText.getFontSpacing();
|
em = pText.getFontSpacing();
|
||||||
|
|
||||||
colors = new int[4];
|
colors = new int[4];
|
||||||
@@ -87,7 +80,7 @@ public class HabitScoreView extends View
|
|||||||
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchScores()
|
protected void fetchData()
|
||||||
{
|
{
|
||||||
|
|
||||||
long toTimestamp = DateHelper.getStartOfToday();
|
long toTimestamp = DateHelper.getStartOfToday();
|
||||||
@@ -102,31 +95,15 @@ public class HabitScoreView extends View
|
|||||||
BUCKET_SIZE * DateHelper.millisecondsInOneDay, 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
|
@Override
|
||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
{
|
{
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
|
|
||||||
float lineHeight = pText.getFontSpacing();
|
float lineHeight = pText.getFontSpacing();
|
||||||
float barHeaderOffset = lineHeight * 0.4f;
|
|
||||||
|
|
||||||
RectF rGrid = new RectF(0, 0, nColumns * columnWidth, columnHeight);
|
RectF rGrid = new RectF(0, 0, nColumns * columnWidth, columnHeight);
|
||||||
rGrid.offset(0, barHeaderHeight);
|
rGrid.offset(0, headerHeight);
|
||||||
drawGrid(canvas, rGrid);
|
drawGrid(canvas, rGrid);
|
||||||
|
|
||||||
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
||||||
@@ -150,7 +127,7 @@ public class HabitScoreView extends View
|
|||||||
|
|
||||||
RectF r = new RectF(0, 0, columnWidth, columnWidth);
|
RectF r = new RectF(0, 0, columnWidth, columnWidth);
|
||||||
r.offset(offset * columnWidth,
|
r.offset(offset * columnWidth,
|
||||||
barHeaderHeight + columnHeight - height - columnWidth / 2);
|
headerHeight + columnHeight - height - columnWidth / 2);
|
||||||
|
|
||||||
if (prevR != null)
|
if (prevR != null)
|
||||||
{
|
{
|
||||||
@@ -163,15 +140,11 @@ public class HabitScoreView extends View
|
|||||||
prevR = r;
|
prevR = r;
|
||||||
|
|
||||||
r = new RectF(0, 0, columnWidth, columnHeight);
|
r = new RectF(0, 0, columnWidth, columnHeight);
|
||||||
r.offset(offset * columnWidth, barHeaderHeight);
|
r.offset(offset * columnWidth, headerHeight);
|
||||||
if (!month.equals(previousMonth))
|
if (!month.equals(previousMonth))
|
||||||
{
|
|
||||||
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||||
}
|
|
||||||
|
|
||||||
previousMonth = month;
|
previousMonth = month;
|
||||||
|
|
||||||
@@ -180,10 +153,6 @@ public class HabitScoreView extends View
|
|||||||
|
|
||||||
private void drawGrid(Canvas canvas, RectF rGrid)
|
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;
|
int nRows = 5;
|
||||||
float rowHeight = rGrid.height() / nRows;
|
float rowHeight = rGrid.height() / nRows;
|
||||||
|
|
||||||
@@ -220,51 +189,4 @@ public class HabitScoreView extends View
|
|||||||
pGraph.setColor(Color.WHITE);
|
pGraph.setColor(Color.WHITE);
|
||||||
canvas.drawOval(rect, pGraph);
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.v4.view.MotionEventCompat;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.isoron.helpers.ColorHelper;
|
import org.isoron.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
@@ -32,29 +29,43 @@ import org.isoron.uhabits.models.Streak;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HabitStreakView extends View
|
public class HabitStreakView extends ScrollableDataView
|
||||||
{
|
{
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
private int columnWidth, columnHeight, nColumns;
|
|
||||||
|
|
||||||
private Paint pText, pBar;
|
private Paint pText, pBar;
|
||||||
private List<Streak> streaks;
|
private List<Streak> streaks;
|
||||||
private int dataOffset;
|
|
||||||
|
|
||||||
private long maxStreakLength;
|
private long maxStreakLength;
|
||||||
|
|
||||||
private int barHeaderHeight;
|
|
||||||
|
|
||||||
private int[] colors;
|
private int[] colors;
|
||||||
private float prevX;
|
|
||||||
private float prevY;
|
|
||||||
|
|
||||||
public HabitStreakView(Context context, Habit habit, int columnWidth)
|
public HabitStreakView(Context context, Habit habit, int columnWidth)
|
||||||
{
|
{
|
||||||
super(context);
|
super(context);
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
this.columnWidth = columnWidth;
|
|
||||||
|
|
||||||
|
setDimensions(columnWidth);
|
||||||
|
createPaints();
|
||||||
|
createColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDimensions(int baseSize)
|
||||||
|
{
|
||||||
|
this.columnWidth = baseSize;
|
||||||
|
columnHeight = 8 * baseSize;
|
||||||
|
headerHeight = baseSize;
|
||||||
|
footerHeight = baseSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createColors()
|
||||||
|
{
|
||||||
|
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 createPaints()
|
||||||
|
{
|
||||||
pText = new Paint();
|
pText = new Paint();
|
||||||
pText.setColor(Color.LTGRAY);
|
pText.setColor(Color.LTGRAY);
|
||||||
pText.setTextAlign(Paint.Align.CENTER);
|
pText.setTextAlign(Paint.Align.CENTER);
|
||||||
@@ -65,21 +76,9 @@ public class HabitStreakView extends View
|
|||||||
pBar.setTextAlign(Paint.Align.CENTER);
|
pBar.setTextAlign(Paint.Align.CENTER);
|
||||||
pBar.setTextSize(columnWidth * 0.5f);
|
pBar.setTextSize(columnWidth * 0.5f);
|
||||||
pBar.setAntiAlias(true);
|
pBar.setAntiAlias(true);
|
||||||
|
|
||||||
columnHeight = 8 * columnWidth;
|
|
||||||
barHeaderHeight = columnWidth;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
fetchStreaks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchStreaks()
|
protected void fetchData()
|
||||||
{
|
{
|
||||||
streaks = habit.getStreaks();
|
streaks = habit.getStreaks();
|
||||||
|
|
||||||
@@ -87,19 +86,6 @@ public class HabitStreakView extends View
|
|||||||
maxStreakLength = Math.max(maxStreakLength, s.length);
|
maxStreakLength = Math.max(maxStreakLength, s.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
@@ -126,7 +112,7 @@ public class HabitStreakView extends View
|
|||||||
|
|
||||||
int height = (int) (columnHeight * lRelative);
|
int height = (int) (columnHeight * lRelative);
|
||||||
Rect r = new Rect(0, 0, columnWidth - 2, height);
|
Rect r = new Rect(0, 0, columnWidth - 2, height);
|
||||||
r.offset(offset * columnWidth, barHeaderHeight + columnHeight - height);
|
r.offset(offset * columnWidth, headerHeight + columnHeight - height);
|
||||||
|
|
||||||
canvas.drawRect(r, pBar);
|
canvas.drawRect(r, pBar);
|
||||||
canvas.drawText(Long.toString(l), r.centerX(), r.top - barHeaderOffset, pBar);
|
canvas.drawText(Long.toString(l), r.centerX(), r.top - barHeaderOffset, pBar);
|
||||||
@@ -137,50 +123,4 @@ public class HabitStreakView extends View
|
|||||||
previousMonth = month;
|
previousMonth = month;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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, Math.min(streaks.size() - nColumns, newDataOffset));
|
|
||||||
|
|
||||||
if (newDataOffset != dataOffset)
|
|
||||||
{
|
|
||||||
dataOffset = newDataOffset;
|
|
||||||
invalidate();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Alinson Santos Xavier
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.v4.view.MotionEventCompat;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public abstract class ScrollableDataView extends View
|
||||||
|
{
|
||||||
|
|
||||||
|
protected int dataOffset;
|
||||||
|
protected int nColumns;
|
||||||
|
protected int columnWidth, columnHeight;
|
||||||
|
protected int headerHeight, footerHeight;
|
||||||
|
|
||||||
|
private float prevX, prevY;
|
||||||
|
|
||||||
|
public ScrollableDataView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void fetchData();
|
||||||
|
|
||||||
|
protected boolean move(float dx)
|
||||||
|
{
|
||||||
|
int newDataOffset = dataOffset + (int) (dx / columnWidth);
|
||||||
|
newDataOffset = Math.max(0, newDataOffset);
|
||||||
|
|
||||||
|
if (newDataOffset != dataOffset)
|
||||||
|
{
|
||||||
|
dataOffset = newDataOffset;
|
||||||
|
fetchData();
|
||||||
|
invalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
setMeasuredDimension(getMeasuredWidth(), columnHeight + headerHeight + footerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||||
|
{
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
nColumns = w / columnWidth;
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user