Refactor custom views

pull/30/head
Alinson S. Xavier 10 years ago
parent e6a5751959
commit 0e5764cf5d

@ -22,52 +22,93 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
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.DateHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class HabitHistoryView extends View
public class HabitHistoryView extends ScrollableDataView
{
private Habit habit;
private int[] checks;
private Context context;
private int[] checkmarks;
private Paint pSquareBg, pSquareFg, pTextHeader;
private int squareSpacing;
private int squareSize, squareSpacing;
private int nColumns, offsetWeeks;
private int colorPrimary, colorPrimaryBrighter, grey;
private Float prevX, prevY;
private float squareTextOffset;
private float headerTextOffset;
private String wdays[];
private SimpleDateFormat dfMonth;
private SimpleDateFormat dfYear;
private Calendar baseDate;
private int nDays;
private int todayWeekday;
private int colors[];
public HabitHistoryView(Context context, Habit habit, int squareSize)
public HabitHistoryView(Context context, Habit habit, int baseSize)
{
super(context);
this.habit = habit;
this.context = context;
this.squareSize = squareSize;
colorPrimary = habit.color;
colorPrimaryBrighter = ColorHelper.mixColors(colorPrimary, Color.WHITE, 0.5f);
grey = Color.rgb(230, 230, 230);
setDimensions(baseSize);
createPaints();
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;
}
private void createPaints()
{
pTextHeader = new Paint();
pTextHeader.setColor(Color.LTGRAY);
pTextHeader.setTextAlign(Align.LEFT);
pTextHeader.setTextSize(squareSize * 0.5f);
pTextHeader.setTextSize(columnWidth * 0.5f);
pTextHeader.setAntiAlias(true);
pSquareBg = new Paint();
@ -76,179 +117,123 @@ public class HabitHistoryView extends View
pSquareFg = new Paint();
pSquareFg.setColor(Color.WHITE);
pSquareFg.setAntiAlias(true);
pSquareFg.setTextSize(squareSize * 0.5f);
pSquareFg.setTextSize(columnWidth * 0.5f);
pSquareFg.setTextAlign(Align.CENTER);
wdays = DateHelper.getShortDayNames();
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
}
@Override
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()
protected void fetchData()
{
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;
long dateTo = DateHelper.getStartOfToday();
for (int i = 0; i < 7 - dayOfWeek; i++)
dateTo += DateHelper.millisecondsInOneDay;
for (int i = 0; i < offsetWeeks * 7; i++)
for (int i = 0; i < dataOffset * 7; i++)
dateTo -= DateHelper.millisecondsInOneDay;
long dateFrom = dateTo;
for (int i = 0; i < nColumns * 7; i++)
dateFrom -= DateHelper.millisecondsInOneDay;
checks = habit.getCheckmarks(dateFrom, dateTo);
checkmarks = habit.getCheckmarks(dateFrom, dateTo);
updateDate();
}
private String previousMonth;
private String previousYear;
private boolean justPrintedYear;
@Override
protected void onDraw(Canvas 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();
currentDate.add(Calendar.DAY_OF_YEAR, -(offsetWeeks - 1) * 7);
int nDays = nColumns * 7;
int todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
currentDate.add(Calendar.DAY_OF_YEAR, -nDays);
currentDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
previousMonth = "";
previousYear = "";
justPrintedYear = false;
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
SimpleDateFormat dfYear = new SimpleDateFormat("yyyy");
GregorianCalendar currentDate = (GregorianCalendar) baseDate.clone();
String previousMonth = "";
String previousYear = "";
for (int column = 0; column < nColumns - 1; column++)
{
drawColumn(canvas, location, currentDate, column);
location.offset(columnWidth, -columnHeight);
}
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)};
drawAxis(canvas, location);
}
float squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
float headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
boolean justPrintedYear = false;
private void drawColumn(Canvas canvas, Rect location, GregorianCalendar date, int column)
{
drawColumnHeader(canvas, location, date);
location.offset(0, columnWidth);
int k = nDays;
for (int i = 0; i < nColumns; i++)
for (int j = 0; j < 7; j++)
{
String month = dfMonth.format(currentDate.getTime());
String year = dfYear.format(currentDate.getTime());
if (!month.equals(previousMonth))
if (!(column == nColumns - 2 && dataOffset == 0 && j > todayWeekday))
{
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;
int checkmarkOffset = nDays - 7 * column - j;
drawSquare(canvas, location, date, checkmarkOffset);
}
date.add(Calendar.DAY_OF_MONTH, 1);
location.offset(0, columnWidth);
}
}
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--;
}
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]]);
square.offset(squareSize, -8 * squareSize);
}
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++)
{
square.offset(0, squareSize);
canvas.drawText(wdays[i], square.left + headerTextOffset,
square.bottom - headerTextOffset, pTextHeader);
location.offset(0, columnWidth);
canvas.drawText(wdays[i], location.left + headerTextOffset,
location.bottom - headerTextOffset, pTextHeader);
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
private void drawColumnHeader(Canvas canvas, Rect location, GregorianCalendar date)
{
int action = event.getAction();
int pointerIndex = MotionEventCompat.getActionIndex(event);
final float x = MotionEventCompat.getX(event, pointerIndex);
final float y = MotionEventCompat.getY(event, pointerIndex);
String month = dfMonth.format(date.getTime());
String year = dfYear.format(date.getTime());
if (action == MotionEvent.ACTION_DOWN)
if (!month.equals(previousMonth))
{
prevX = x;
prevY = y;
}
int offset = 0;
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;
float dy = y - prevY;
if (Math.abs(dy) > Math.abs(dx)) return false;
getParent().requestDisallowInterceptTouchEvent(true);
if (move(dx))
{
prevX = x;
prevY = y;
}
canvas.drawText(year, location.left, location.bottom - headerTextOffset, pTextHeader);
previousYear = year;
justPrintedYear = true;
}
return true;
}
private boolean move(float dx)
{
int newOffsetWeeks = offsetWeeks + (int) (dx / squareSize);
newOffsetWeeks = Math.max(0, newOffsetWeeks);
if (newOffsetWeeks != offsetWeeks)
else
{
offsetWeeks = newOffsetWeeks;
fetchReps();
invalidate();
return true;
justPrintedYear = false;
}
else return false;
}
}

@ -21,9 +21,6 @@ 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;
@ -33,30 +30,23 @@ import org.isoron.uhabits.models.Score;
import java.text.SimpleDateFormat;
import java.util.List;
public class HabitScoreView extends View
public class HabitScoreView extends ScrollableDataView
{
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);
@ -75,8 +65,11 @@ public class HabitScoreView extends View
pGrid.setAntiAlias(true);
pGrid.setStrokeWidth(columnWidth * 0.05f);
this.columnWidth = columnWidth;
columnHeight = 8 * columnWidth;
barHeaderHeight = columnWidth;
headerHeight = columnWidth;
footerHeight = columnWidth;
em = pText.getFontSpacing();
colors = new int[4];
@ -87,7 +80,7 @@ public class HabitScoreView extends View
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
}
private void fetchScores()
protected void fetchData()
{
long toTimestamp = DateHelper.getStartOfToday();
@ -102,31 +95,15 @@ public class HabitScoreView extends View
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);
rGrid.offset(0, headerHeight);
drawGrid(canvas, rGrid);
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
@ -150,7 +127,7 @@ public class HabitScoreView extends View
RectF r = new RectF(0, 0, columnWidth, columnWidth);
r.offset(offset * columnWidth,
barHeaderHeight + columnHeight - height - columnWidth / 2);
headerHeight + columnHeight - height - columnWidth / 2);
if (prevR != null)
{
@ -163,15 +140,11 @@ public class HabitScoreView extends View
prevR = r;
r = new RectF(0, 0, columnWidth, columnHeight);
r.offset(offset * columnWidth, barHeaderHeight);
r.offset(offset * columnWidth, headerHeight);
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;
@ -180,10 +153,6 @@ public class HabitScoreView extends View
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;
@ -220,51 +189,4 @@ public class HabitScoreView extends View
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;
}
}

@ -21,9 +21,6 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
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.uhabits.models.Habit;
@ -32,29 +29,43 @@ import org.isoron.uhabits.models.Streak;
import java.text.SimpleDateFormat;
import java.util.List;
public class HabitStreakView extends View
public class HabitStreakView extends ScrollableDataView
{
private Habit habit;
private int columnWidth, columnHeight, nColumns;
private Paint pText, pBar;
private List<Streak> streaks;
private int dataOffset;
private long maxStreakLength;
private int barHeaderHeight;
private int[] colors;
private float prevX;
private float prevY;
public HabitStreakView(Context context, Habit habit, int columnWidth)
{
super(context);
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.setColor(Color.LTGRAY);
pText.setTextAlign(Paint.Align.CENTER);
@ -65,21 +76,9 @@ public class HabitStreakView extends View
pBar.setTextAlign(Paint.Align.CENTER);
pBar.setTextSize(columnWidth * 0.5f);
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();
@ -87,19 +86,6 @@ public class HabitStreakView extends View
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
protected void onDraw(Canvas canvas)
@ -126,7 +112,7 @@ public class HabitStreakView extends View
int height = (int) (columnHeight * lRelative);
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.drawText(Long.toString(l), r.centerX(), r.top - barHeaderOffset, pBar);
@ -137,50 +123,4 @@ public class HabitStreakView extends View
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();
}
}
Loading…
Cancel
Save