Allow custom views to be rendered on the layout editor

pull/30/head
Alinson S. Xavier 10 years ago
parent f511ca2028
commit d2682358c2

@ -17,21 +17,18 @@
package org.isoron.helpers;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import org.isoron.uhabits.R;
public abstract class DialogHelper
{
public static final String ISORON_NAMESPACE = "http://isoron.org/android";
private static Typeface fontawesome;
public interface OnSavedListener
@ -65,4 +62,14 @@ public abstract class DialogHelper
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getInt("launch_count", 0);
}
public static String getAttribute(Context context, AttributeSet attrs, String name)
{
int resId = attrs.getAttributeResourceValue(ISORON_NAMESPACE, name, 0);
if(resId != 0)
return context.getResources().getString(resId);
else
return attrs.getAttributeValue(ISORON_NAMESPACE, name);
}
}

@ -25,15 +25,14 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.HabitHistoryView;
import org.isoron.uhabits.views.HabitScoreView;
@ -71,29 +70,21 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview);
TextView tvStrength = (TextView) view.findViewById(R.id.tvStrength);
TextView tvStreaks = (TextView) view.findViewById(R.id.tvStreaks);
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
HabitStreakView streakView = (HabitStreakView) view.findViewById(R.id.streakView);
HabitScoreView scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
HabitHistoryView historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
tvHistory.setTextColor(habit.color);
tvOverview.setTextColor(habit.color);
tvStrength.setTextColor(habit.color);
tvStreaks.setTextColor(habit.color);
LinearLayout llOverview = (LinearLayout) view.findViewById(R.id.llOverview);
llOverview.addView(new RingView(activity,
(int) activity.getResources().getDimension(R.dimen.small_square_size) * 4,
habit.color, ((float) habit.getScore() / Habit.MAX_SCORE), activity.getString(R.string.habit_strength)));
LinearLayout llStrength = (LinearLayout) view.findViewById(R.id.llStrength);
llStrength.addView(new HabitScoreView(activity, habit,
(int) activity.getResources().getDimension(R.dimen.small_square_size)));
LinearLayout llHistory = (LinearLayout) view.findViewById(R.id.llHistory);
HabitHistoryView hhv = new HabitHistoryView(activity, habit,
(int) activity.getResources().getDimension(R.dimen.small_square_size));
llHistory.addView(hhv);
LinearLayout llStreaks = (LinearLayout) view.findViewById(R.id.llStreaks);
HabitStreakView hsv = new HabitStreakView(activity, habit,
(int) activity.getResources().getDimension(R.dimen.small_square_size));
llStreaks.addView(hsv);
scoreRing.setColor(habit.color);
scoreRing.setPercentage((float) habit.getScore() / Habit.MAX_SCORE);
streakView.setHabit(habit);
scoreView.setHabit(habit);
historyView.setHabit(habit);
setHasOptionsMenu(true);
return view;

@ -22,19 +22,21 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.util.AttributeSet;
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;
import java.util.Locale;
import java.util.Random;
public class HabitHistoryView extends ScrollableDataView
{
private Habit habit;
private int[] checkmarks;
private Paint pSquareBg, pSquareFg, pTextHeader;
@ -52,13 +54,29 @@ public class HabitHistoryView extends ScrollableDataView
private int todayWeekday;
private int colors[];
private Rect baseLocation;
private int baseSize;
private int primaryColor;
public HabitHistoryView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.primaryColor = ColorHelper.palette[7];
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
init();
}
public HabitHistoryView(Context context, Habit habit, int baseSize)
public void setHabit(Habit habit)
{
super(context);
this.habit = habit;
this.primaryColor = habit.color;
createColors();
fetchData();
postInvalidate();
}
setDimensions(baseSize);
private void init()
{
setDimensions(this.baseSize);
createPaints();
createColors();
@ -90,7 +108,6 @@ public class HabitHistoryView extends ScrollableDataView
private void createColors()
{
int primaryColor = habit.color;
int primaryColorBright = ColorHelper.mixColors(primaryColor, Color.WHITE, 0.5f);
int grey = Color.rgb(230, 230, 230);
@ -116,7 +133,7 @@ public class HabitHistoryView extends ScrollableDataView
pTextHeader.setAntiAlias(true);
pSquareBg = new Paint();
pSquareBg.setColor(habit.color);
pSquareBg.setColor(primaryColor);
pSquareFg = new Paint();
pSquareFg.setColor(Color.WHITE);
@ -130,10 +147,41 @@ public class HabitHistoryView extends ScrollableDataView
protected void fetchData()
{
checkmarks = habit.getAllCheckmarks();
if(isInEditMode())
generateRandomData();
else
{
if(habit == null)
{
checkmarks = new int[0];
return;
}
checkmarks = habit.getAllCheckmarks();
}
updateDate();
}
private void generateRandomData()
{
Random random = new Random();
checkmarks = new int[100];
for(int i = 0; i < 100; i++)
if(random.nextFloat() < 0.3) checkmarks[i] = 2;
for(int i = 0; i < 100 - 7; i++)
{
int count = 0;
for (int j = 0; j < 7; j++)
if(checkmarks[i + j] != 0)
count++;
if(count >= 3) checkmarks[i] = Math.max(checkmarks[i], 1);
}
}
private String previousMonth;
private String previousYear;
private boolean justPrintedYear;

@ -21,76 +21,132 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
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.Locale;
import java.util.Random;
public class HabitScoreView extends ScrollableDataView
{
public static final int BUCKET_SIZE = 7;
private final Paint pGrid;
private final float em;
private Paint pGrid;
private float em;
private Habit habit;
private SimpleDateFormat dfMonth;
private SimpleDateFormat dfDay;
private Paint pText, pGraph;
private RectF rect, prevRect;
private int baseSize;
private int[] colors;
private int[] scores;
private int primaryColor;
public HabitScoreView(Context context, Habit habit, int columnWidth)
public HabitScoreView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
this.primaryColor = ColorHelper.palette[7];
init();
}
public void setHabit(Habit habit)
{
super(context);
this.habit = habit;
this.primaryColor = habit.color;
fetchData();
postInvalidate();
}
private void init()
{
createPaints();
setDimensions();
createColors();
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
dfDay = new SimpleDateFormat("d", Locale.getDefault());
rect = new RectF();
prevRect = new RectF();
}
private void setDimensions()
{
this.columnWidth = baseSize;
columnHeight = 8 * baseSize;
headerHeight = baseSize;
footerHeight = baseSize;
em = pText.getFontSpacing();
}
private void createColors()
{
colors = new int[4];
colors[0] = Color.rgb(230, 230, 230);
colors[3] = primaryColor;
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.LEFT);
pText.setTextSize(columnWidth * 0.5f);
pText.setTextSize(baseSize * 0.5f);
pText.setAntiAlias(true);
pGraph = new Paint();
pGraph.setTextAlign(Paint.Align.CENTER);
pGraph.setTextSize(columnWidth * 0.5f);
pGraph.setTextSize(baseSize * 0.5f);
pGraph.setAntiAlias(true);
pGraph.setStrokeWidth(columnWidth * 0.1f);
pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid = new Paint();
pGrid.setColor(Color.LTGRAY);
pGrid.setAntiAlias(true);
pGrid.setStrokeWidth(columnWidth * 0.05f);
this.columnWidth = columnWidth;
columnHeight = 8 * columnWidth;
headerHeight = columnWidth;
footerHeight = columnWidth;
em = pText.getFontSpacing();
colors = new int[4];
pGrid.setStrokeWidth(baseSize * 0.05f);
}
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);
protected void fetchData()
{
if(isInEditMode())
generateRandomData();
else
{
if (habit == null)
{
scores = new int[0];
return;
}
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
dfDay = new SimpleDateFormat("d", Locale.getDefault());
scores = habit.getAllScores(BUCKET_SIZE * DateHelper.millisecondsInOneDay);
}
rect = new RectF();
prevRect = new RectF();
}
protected void fetchData()
private void generateRandomData()
{
scores = habit.getAllScores(BUCKET_SIZE * DateHelper.millisecondsInOneDay);
Random random = new Random();
scores = new int[100];
scores[0] = Habit.MAX_SCORE / 2;
for(int i = 1; i < 100; i++)
{
int step = Habit.MAX_SCORE / 10;
scores[i] = scores[i - 1] + random.nextInt(step * 2) - step;
scores[i] = Math.max(0, Math.min(Habit.MAX_SCORE, scores[i]));
}
}
@Override
@ -106,7 +162,7 @@ public class HabitScoreView extends ScrollableDataView
String previousMonth = "";
pGraph.setColor(habit.color);
pGraph.setColor(primaryColor);
prevRect.setEmpty();
long currentDate = DateHelper.getStartOfToday();
@ -171,7 +227,7 @@ public class HabitScoreView extends ScrollableDataView
private void drawLine(Canvas canvas, RectF rectFrom, RectF rectTo)
{
pGraph.setColor(habit.color);
pGraph.setColor(primaryColor);
canvas.drawLine(rectFrom.centerX(), rectFrom.centerY(), rectTo.centerX(), rectTo.centerY(),
pGraph);
}
@ -183,7 +239,7 @@ public class HabitScoreView extends ScrollableDataView
canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
pGraph.setColor(habit.color);
pGraph.setColor(primaryColor);
canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);

@ -21,31 +21,55 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Streak;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.Random;
public class HabitStreakView extends ScrollableDataView
{
private Habit habit;
private Paint pText, pBar;
private List<Streak> streaks;
private long[] startTimes;
private long[] endTimes;
private long[] lengths;
private long maxStreakLength;
private int[] colors;
private SimpleDateFormat dfMonth;
private Rect rect;
private int baseSize;
private int primaryColor;
public HabitStreakView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
this.primaryColor = ColorHelper.palette[7];
init();
}
public HabitStreakView(Context context, Habit habit, int columnWidth)
public void setHabit(Habit habit)
{
super(context);
this.habit = habit;
this.primaryColor = habit.color;
createColors();
fetchData();
postInvalidate();
}
setDimensions(columnWidth);
private void init()
{
setDimensions(baseSize);
createPaints();
createColors();
@ -65,7 +89,7 @@ public class HabitStreakView extends ScrollableDataView
{
colors = new int[4];
colors[0] = Color.rgb(230, 230, 230);
colors[3] = habit.color;
colors[3] = primaryColor;
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
}
@ -86,12 +110,59 @@ public class HabitStreakView extends ScrollableDataView
protected void fetchData()
{
streaks = habit.getStreaks();
for (Streak s : streaks)
maxStreakLength = Math.max(maxStreakLength, s.length);
if(isInEditMode())
generateRandomData();
else
{
if(habit == null)
{
startTimes = endTimes = lengths = new long[0];
return;
}
List<Streak> streaks = habit.getStreaks();
int size = streaks.size();
startTimes = new long[size];
endTimes = new long[size];
lengths = new long[size];
int k = 0;
for (Streak s : streaks)
{
startTimes[k] = s.start;
endTimes[k] = s.end;
lengths[k] = s.length;
k++;
maxStreakLength = Math.max(maxStreakLength, s.length);
}
}
}
private void generateRandomData()
{
int size = 30;
startTimes = new long[size];
endTimes = new long[size];
lengths = new long[size];
Random random = new Random();
Long date = DateHelper.getStartOfToday();
for(int i = 0; i < size; i++)
{
int l = (int) Math.pow(2, random.nextFloat() * 5 + 1);
endTimes[i] = date;
date -= l * DateHelper.millisecondsInOneDay;
lengths[i] = (long) l;
startTimes[i] = date;
maxStreakLength = Math.max(maxStreakLength, l);
}
}
@Override
protected void onDraw(Canvas canvas)
@ -101,7 +172,7 @@ public class HabitStreakView extends ScrollableDataView
float lineHeight = pText.getFontSpacing();
float barHeaderOffset = lineHeight * 0.4f;
int nStreaks = streaks.size();
int nStreaks = startTimes.length;
int start = nStreaks - nColumns - dataOffset;
String previousMonth = "";
@ -109,9 +180,9 @@ public class HabitStreakView extends ScrollableDataView
for (int offset = 0; offset < nColumns && start + offset < nStreaks; offset++)
{
if(start + offset < 0) continue;
String month = dfMonth.format(streaks.get(start + offset).start);
String month = dfMonth.format(startTimes[start + offset]);
long l = streaks.get(offset + start).length;
long l = lengths[offset + start];
double lRelative = ((double) l) / maxStreakLength;
pBar.setColor(colors[(int) Math.floor(lRelative * 3)]);

@ -21,34 +21,57 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.R;
public class RingView extends View
{
private int size;
private int color;
private float perc;
private float percentage;
private Paint pRing;
private float lineHeight;
private String label;
private RectF rect;
public RingView(Context context, int size, int color, float perc, String label)
public RingView(Context context, AttributeSet attrs)
{
super(context, attrs);
this.size = (int) context.getResources().getDimension(R.dimen.small_square_size) * 4;
this.label = DialogHelper.getAttribute(context, attrs, "label");
this.color = ColorHelper.palette[7];
this.percentage = 0.75f;
init();
}
public void setColor(int color)
{
super(context);
this.size = size;
this.color = color;
this.perc = perc;
pRing.setColor(color);
postInvalidate();
}
public void setPercentage(float percentage)
{
this.percentage = percentage;
postInvalidate();
}
private void init()
{
pRing = new Paint();
pRing.setColor(color);
pRing.setAntiAlias(true);
pRing.setColor(color);
pRing.setTextAlign(Paint.Align.CENTER);
pRing.setTextSize(size * 0.2f);
lineHeight = pRing.getFontSpacing();
rect = new RectF();
this.label = label;
}
@Override
@ -66,10 +89,10 @@ public class RingView extends View
pRing.setColor(color);
rect.set(0, 0, size, size);
canvas.drawArc(rect, -90, 360 * perc, true, pRing);
canvas.drawArc(rect, -90, 360 * percentage, true, pRing);
pRing.setColor(Color.rgb(230, 230, 230));
canvas.drawArc(rect, 360 * perc - 90 + 2, 360 * (1 - perc) - 4, true, pRing);
canvas.drawArc(rect, 360 * percentage - 90 + 2, 360 * (1 - percentage) - 4, true, pRing);
pRing.setColor(Color.WHITE);
rect.inset(thickness, thickness);
@ -77,8 +100,7 @@ public class RingView extends View
pRing.setColor(Color.GRAY);
pRing.setTextSize(size * 0.2f);
lineHeight = pRing.getFontSpacing();
canvas.drawText(String.format("%.0f%%", perc * 100), rect.centerX(),
canvas.drawText(String.format("%.0f%%", percentage * 100), rect.centerX(),
rect.centerY() + lineHeight / 3, pRing);
pRing.setTextSize(size * 0.15f);

@ -21,6 +21,7 @@ package org.isoron.uhabits.views;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
@ -42,7 +43,17 @@ public abstract class ScrollableDataView extends View implements GestureDetector
public ScrollableDataView(Context context)
{
super(context);
init(context);
}
public ScrollableDataView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context);
}
private void init(Context context)
{
detector = new GestureDetector(context, this);
scroller = new Scroller(context, null, true);
scrollAnimator = ValueAnimator.ofFloat(0, 1);

@ -1,5 +1,6 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://isoron.org/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/windowBackground"
@ -19,6 +20,12 @@
style="@style/cardHeaderStyle"
android:text="@string/overview"/>
<org.isoron.uhabits.views.RingView
android:id="@+id/scoreRing"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:label="@string/habit_strength"/>
</LinearLayout>
<LinearLayout
@ -31,6 +38,11 @@
style="@style/cardHeaderStyle"
android:text="@string/habit_strength"/>
<org.isoron.uhabits.views.HabitScoreView
android:id="@+id/scoreView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout style="@style/cardStyle">
@ -45,6 +57,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"/>
<org.isoron.uhabits.views.HabitHistoryView
android:id="@+id/historyView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout style="@style/cardStyle">
@ -59,6 +77,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"/>
<org.isoron.uhabits.views.HabitStreakView
android:id="@+id/streakView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

@ -4,12 +4,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.isoron.uhabits.ShowHabitActivity"
tools:ignore="MergeRootFrame" >
tools:ignore="MergeRootFrame"
tools:menu="show_habit_activity_menu,show_habit_fragment_menu">
<fragment
android:id="@+id/fragment2"
android:name="org.isoron.uhabits.fragments.ShowHabitFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:layout="@layout/show_habit"
android:layout_gravity="center"/>
</FrameLayout>

Loading…
Cancel
Save