Refactor custom views; fix rendering issues

pull/30/head
Alinson S. Xavier 10 years ago
parent f0d12e9925
commit b8cacaffa9

@ -28,7 +28,7 @@ import android.widget.RemoteViews;
import org.isoron.helpers.DialogHelper; import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.HabitHistoryView; import org.isoron.uhabits.views.HabitScoreView;
public class HabitWidgetProvider extends AppWidgetProvider public class HabitWidgetProvider extends AppWidgetProvider
{ {
@ -63,9 +63,10 @@ public class HabitWidgetProvider extends AppWidgetProvider
Habit habit = Habit.get(habitId); Habit habit = Habit.get(habitId);
// SmallWidgetView widgetView = new SmallWidgetView(context); // SmallWidgetView widgetView = new SmallWidgetView(context);
HabitHistoryView widgetView = new HabitHistoryView(context, null); // HabitHistoryView widgetView = new HabitHistoryView(context, null);
// HabitScoreView widgetView = new HabitScoreView(context, null); HabitScoreView widgetView = new HabitScoreView(context, null);
// HabitStreakView widgetView = new HabitStreakView(context, null); // HabitStreakView widgetView = new HabitStreakView(context, null);
widgetView.setIsBackgroundTransparent(true);
widgetView.setHabit(habit); widgetView.setHabit(habit);
widgetView.setDrawingCacheEnabled(true); widgetView.setDrawingCacheEnabled(true);
widgetView.measure(max_width, max_height); widgetView.measure(max_width, max_height);

@ -26,7 +26,6 @@ import android.util.AttributeSet;
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;
@ -45,6 +44,11 @@ public class HabitHistoryView extends ScrollableDataView
private float squareTextOffset; private float squareTextOffset;
private float headerTextOffset; private float headerTextOffset;
private int columnWidth;
private int columnHeight;
private int nColumns;
private int baseSize;
private String wdays[]; private String wdays[];
private SimpleDateFormat dfMonth; private SimpleDateFormat dfMonth;
private SimpleDateFormat dfYear; private SimpleDateFormat dfYear;
@ -76,7 +80,6 @@ public class HabitHistoryView extends ScrollableDataView
{ {
createPaints(); createPaints();
createColors(); createColors();
updateDimensions();
wdays = DateHelper.getShortDayNames(); wdays = DateHelper.getShortDayNames();
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault()); dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
@ -88,7 +91,7 @@ public class HabitHistoryView extends ScrollableDataView
private void updateDate() private void updateDate()
{ {
baseDate = new GregorianCalendar(); baseDate = new GregorianCalendar();
baseDate.add(Calendar.DAY_OF_YEAR, -(dataOffset - 1) * 7); baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7);
nDays = (nColumns - 1) * 7; nDays = (nColumns - 1) * 7;
todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7; todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
@ -98,9 +101,34 @@ public class HabitHistoryView extends ScrollableDataView
} }
@Override @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{ {
super.onSizeChanged(w, h, oldw, oldh); int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int b = height / 8;
height = b * 8;
width = (width / b) * b;
setMeasuredDimension(width, height);
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
baseSize = height / 8;
setScrollerBucketSize(baseSize);
columnWidth = baseSize;
columnHeight = 8 * baseSize;
nColumns = width / baseSize;
squareSpacing = baseSize / 10;
pSquareFg.setTextSize(baseSize * 0.5f);
pTextHeader.setTextSize(baseSize * 0.5f);
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
updateDate(); updateDate();
} }
@ -116,15 +144,6 @@ public class HabitHistoryView extends ScrollableDataView
colors[2] = primaryColor; colors[2] = primaryColor;
} }
protected void updateDimensions()
{
squareSpacing = columnWidth / 10;
pSquareFg.setTextSize(columnWidth * 0.5f);
pTextHeader.setTextSize(columnWidth * 0.5f);
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
}
protected void createPaints() protected void createPaints()
{ {
pTextHeader = new Paint(); pTextHeader = new Paint();
@ -212,9 +231,9 @@ public class HabitHistoryView extends ScrollableDataView
for (int j = 0; j < 7; j++) for (int j = 0; j < 7; j++)
{ {
if (!(column == nColumns - 2 && dataOffset == 0 && j > todayWeekday)) if (!(column == nColumns - 2 && getDataOffset() == 0 && j > todayWeekday))
{ {
int checkmarkOffset = dataOffset * 7 + nDays - 7 * (column + 1) + todayWeekday - j; int checkmarkOffset = getDataOffset() * 7 + nDays - 7 * (column + 1) + todayWeekday - j;
drawSquare(canvas, location, date, checkmarkOffset); drawSquare(canvas, location, date, checkmarkOffset);
} }

@ -27,7 +27,6 @@ import android.util.AttributeSet;
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;
@ -37,6 +36,10 @@ import java.util.Random;
public class HabitScoreView extends ScrollableDataView public class HabitScoreView extends ScrollableDataView
{ {
public static final int BUCKET_SIZE = 7; public static final int BUCKET_SIZE = 7;
public static final PorterDuffXfermode XFERMODE_CLEAR =
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
public static final PorterDuffXfermode XFERMODE_SRC =
new PorterDuffXfermode(PorterDuff.Mode.SRC);
private Paint pGrid; private Paint pGrid;
private float em; private float em;
@ -48,14 +51,18 @@ public class HabitScoreView extends ScrollableDataView
private RectF rect, prevRect; private RectF rect, prevRect;
private int baseSize; private int baseSize;
private int columnWidth;
private int columnHeight;
private int nColumns;
private int[] colors; private int[] colors;
private int[] scores; private int[] scores;
private int primaryColor; private int primaryColor;
private boolean isBackgroundTransparent;
public HabitScoreView(Context context, AttributeSet attrs) public HabitScoreView(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
init(); init();
} }
@ -72,7 +79,6 @@ public class HabitScoreView extends ScrollableDataView
{ {
createPaints(); createPaints();
createColors(); createColors();
updateDimensions();
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault()); dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
dfDay = new SimpleDateFormat("d", Locale.getDefault()); dfDay = new SimpleDateFormat("d", Locale.getDefault());
@ -109,18 +115,33 @@ public class HabitScoreView extends ScrollableDataView
} }
@Override @Override
protected void updateDimensions() protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{ {
this.columnWidth = baseSize; int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int b = height / 9;
height = b * 9;
width = (width / b) * b;
setMeasuredDimension(width, height);
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
baseSize = height / 9;
setScrollerBucketSize(baseSize);
columnWidth = baseSize;
columnHeight = 8 * baseSize; columnHeight = 8 * baseSize;
headerHeight = baseSize; nColumns = width / baseSize;
footerHeight = baseSize;
em = pText.getFontSpacing();
pText.setTextSize(baseSize * 0.5f); pText.setTextSize(baseSize * 0.5f);
pGraph.setTextSize(baseSize * 0.5f); pGraph.setTextSize(baseSize * 0.5f);
pGraph.setStrokeWidth(baseSize * 0.1f); pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid.setStrokeWidth(baseSize * 0.05f); pGrid.setStrokeWidth(baseSize * 0.05f);
em = pText.getFontSpacing();
} }
protected void fetchData() protected void fetchData()
@ -162,7 +183,6 @@ public class HabitScoreView extends ScrollableDataView
float lineHeight = pText.getFontSpacing(); float lineHeight = pText.getFontSpacing();
rect.set(0, 0, nColumns * columnWidth, columnHeight); rect.set(0, 0, nColumns * columnWidth, columnHeight);
rect.offset(0, headerHeight);
drawGrid(canvas, rect); drawGrid(canvas, rect);
String previousMonth = ""; String previousMonth = "";
@ -172,7 +192,7 @@ public class HabitScoreView extends ScrollableDataView
long currentDate = DateHelper.getStartOfToday(); long currentDate = DateHelper.getStartOfToday();
for(int k = 0; k < nColumns + dataOffset - 1; k++) for(int k = 0; k < nColumns + getDataOffset() - 1; k++)
currentDate -= 7 * DateHelper.millisecondsInOneDay; currentDate -= 7 * DateHelper.millisecondsInOneDay;
for (int k = 0; k < nColumns; k++) for (int k = 0; k < nColumns; k++)
@ -181,15 +201,14 @@ public class HabitScoreView extends ScrollableDataView
String day = dfDay.format(currentDate); String day = dfDay.format(currentDate);
int score = 0; int score = 0;
int offset = nColumns - k - 1 + dataOffset; int offset = nColumns - k - 1 + getDataOffset();
if(offset < scores.length) score = scores[offset]; if(offset < scores.length) score = scores[offset];
double sRelative = ((double) score) / Habit.MAX_SCORE; double sRelative = ((double) score) / Habit.MAX_SCORE;
int height = (int) (columnHeight * sRelative); int height = (int) (columnHeight * sRelative);
rect.set(0, 0, columnWidth, columnWidth); rect.set(0, 0, baseSize, baseSize);
rect.offset(k * columnWidth, rect.offset(k * columnWidth, columnHeight - height - columnWidth / 2);
headerHeight + columnHeight - height - columnWidth / 2);
if (!prevRect.isEmpty()) if (!prevRect.isEmpty())
{ {
@ -202,7 +221,7 @@ public class HabitScoreView extends ScrollableDataView
prevRect.set(rect); prevRect.set(rect);
rect.set(0, 0, columnWidth, columnHeight); rect.set(0, 0, columnWidth, columnHeight);
rect.offset(k * columnWidth, headerHeight); rect.offset(k * columnWidth, 0);
if (!month.equals(previousMonth)) if (!month.equals(previousMonth))
canvas.drawText(month, rect.centerX(), rect.bottom + lineHeight * 1.2f, pText); canvas.drawText(month, rect.centerX(), rect.bottom + lineHeight * 1.2f, pText);
else else
@ -240,18 +259,31 @@ public class HabitScoreView extends ScrollableDataView
private void drawMarker(Canvas canvas, RectF rect) private void drawMarker(Canvas canvas, RectF rect)
{ {
rect.inset(columnWidth * 0.15f, columnWidth * 0.15f); rect.inset(columnWidth * 0.15f, columnWidth * 0.15f);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f); rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
pGraph.setColor(primaryColor); setModeOrColor(pGraph, XFERMODE_SRC, primaryColor);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f); rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); if(isBackgroundTransparent)
pGraph.setXfermode(XFERMODE_SRC);
}
public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
{
this.isBackgroundTransparent = isBackgroundTransparent;
}
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
{
if(isBackgroundTransparent)
p.setXfermode(mode);
else
p.setColor(color);
} }
} }

@ -43,6 +43,11 @@ public class HabitStreakView extends ScrollableDataView
private long[] endTimes; private long[] endTimes;
private long[] lengths; private long[] lengths;
private int columnWidth;
private int columnHeight;
private int headerHeight;
private int nColumns;
private long maxStreakLength; private long maxStreakLength;
private int[] colors; private int[] colors;
private SimpleDateFormat dfMonth; private SimpleDateFormat dfMonth;
@ -53,7 +58,6 @@ public class HabitStreakView extends ScrollableDataView
public HabitStreakView(Context context, AttributeSet attrs) public HabitStreakView(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
init(); init();
} }
@ -62,6 +66,7 @@ public class HabitStreakView extends ScrollableDataView
{ {
this.habit = habit; this.habit = habit;
this.primaryColor = habit.color; this.primaryColor = habit.color;
createColors(); createColors();
fetchData(); fetchData();
postInvalidate(); postInvalidate();
@ -69,7 +74,6 @@ public class HabitStreakView extends ScrollableDataView
private void init() private void init()
{ {
setDimensions(baseSize);
createPaints(); createPaints();
createColors(); createColors();
@ -77,12 +81,32 @@ public class HabitStreakView extends ScrollableDataView
rect = new Rect(); rect = new Rect();
} }
private void setDimensions(int baseSize) @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int b = height / 10;
height = b * 10;
width = (width / b) * b;
setMeasuredDimension(width, height);
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{ {
this.columnWidth = baseSize; baseSize = height / 10;
setScrollerBucketSize(baseSize);
columnWidth = baseSize;
columnHeight = 8 * baseSize; columnHeight = 8 * baseSize;
headerHeight = baseSize; headerHeight = baseSize;
footerHeight = baseSize; nColumns = width / baseSize;
pText.setTextSize(baseSize * 0.5f);
pBar.setTextSize(baseSize * 0.5f);
} }
private void createColors() private void createColors()
@ -91,8 +115,8 @@ public class HabitStreakView extends ScrollableDataView
colors[3] = primaryColor; colors[3] = primaryColor;
colors[1] = Color.argb(80, Color.red(primaryColor), Color.green(primaryColor), Color.blue( colors[1] = Color.argb(80, Color.red(primaryColor), Color.green(primaryColor), Color.blue(
primaryColor)); primaryColor));
colors[2] = Color.argb(170, Color.red(primaryColor), Color.green(primaryColor), Color.blue( colors[2] = Color.argb(170, Color.red(primaryColor), Color.green(primaryColor),
primaryColor)); Color.blue(primaryColor));
colors[0] = Color.argb(30, 0, 0, 0); colors[0] = Color.argb(30, 0, 0, 0);
} }
@ -101,12 +125,10 @@ public class HabitStreakView extends ScrollableDataView
pText = new Paint(); pText = new Paint();
pText.setColor(Color.LTGRAY); pText.setColor(Color.LTGRAY);
pText.setTextAlign(Paint.Align.CENTER); pText.setTextAlign(Paint.Align.CENTER);
pText.setTextSize(columnWidth * 0.5f);
pText.setAntiAlias(true); pText.setAntiAlias(true);
pBar = new Paint(); pBar = new Paint();
pBar.setTextAlign(Paint.Align.CENTER); pBar.setTextAlign(Paint.Align.CENTER);
pBar.setTextSize(columnWidth * 0.5f);
pBar.setAntiAlias(true); pBar.setAntiAlias(true);
} }
@ -175,7 +197,7 @@ public class HabitStreakView extends ScrollableDataView
float barHeaderOffset = lineHeight * 0.4f; float barHeaderOffset = lineHeight * 0.4f;
int nStreaks = startTimes.length; int nStreaks = startTimes.length;
int start = nStreaks - nColumns - dataOffset; int start = nStreaks - nColumns - getDataOffset();
String previousMonth = ""; String previousMonth = "";

@ -22,22 +22,17 @@ package org.isoron.uhabits.views;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.Scroller; import android.widget.Scroller;
import org.isoron.uhabits.R;
public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener, public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener,
ValueAnimator.AnimatorUpdateListener ValueAnimator.AnimatorUpdateListener
{ {
protected int dataOffset; private int dataOffset;
protected int nColumns, nRows; private int scrollerBucketSize;
protected int columnWidth, columnHeight;
protected int headerHeight, footerHeight;
private GestureDetector detector; private GestureDetector detector;
private Scroller scroller; private Scroller scroller;
@ -57,7 +52,6 @@ public abstract class ScrollableDataView extends View implements GestureDetector
private void init(Context context) private void init(Context context)
{ {
this.columnWidth = (int) context.getResources().getDimension(R.dimen.small_square_size);
detector = new GestureDetector(context, this); detector = new GestureDetector(context, this);
scroller = new Scroller(context, null, true); scroller = new Scroller(context, null, true);
scrollAnimator = ValueAnimator.ofFloat(0, 1); scrollAnimator = ValueAnimator.ofFloat(0, 1);
@ -72,39 +66,6 @@ public abstract class ScrollableDataView extends View implements GestureDetector
return detector.onTouchEvent(event); return detector.onTouchEvent(event);
} }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
Log.d("ScrollableDataView", String.format("onMeasure width=%d height=%d", width, height));
columnWidth = height / 8;
columnHeight = columnWidth * 8 + footerHeight + headerHeight;
height = columnHeight;
width = (width / columnWidth) * columnWidth;
setMeasuredDimension(width, height);
updateDimensions();
}
protected void updateDimensions()
{
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
nColumns = w / columnWidth;
fetchData();
}
@Override @Override
public boolean onDown(MotionEvent e) public boolean onDown(MotionEvent e)
{ {
@ -126,13 +87,17 @@ public abstract class ScrollableDataView extends View implements GestureDetector
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy)
{ {
if(scrollerBucketSize == 0)
return false;
if(Math.abs(dx) > Math.abs(dy)) if(Math.abs(dx) > Math.abs(dy))
getParent().requestDisallowInterceptTouchEvent(true); getParent().requestDisallowInterceptTouchEvent(true);
scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) -dx, (int) dy, 0); scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) -dx, (int) dy, 0);
scroller.computeScrollOffset(); scroller.computeScrollOffset();
dataOffset = Math.max(0, scroller.getCurrX() / columnWidth); dataOffset = Math.max(0, scroller.getCurrX() / scrollerBucketSize);
postInvalidate(); postInvalidate();
return true; return true;
} }
@ -161,7 +126,7 @@ public abstract class ScrollableDataView extends View implements GestureDetector
if (!scroller.isFinished()) if (!scroller.isFinished())
{ {
scroller.computeScrollOffset(); scroller.computeScrollOffset();
dataOffset = Math.max(0, scroller.getCurrX() / columnWidth); dataOffset = Math.max(0, scroller.getCurrX() / scrollerBucketSize);
postInvalidate(); postInvalidate();
} }
else else
@ -169,4 +134,14 @@ public abstract class ScrollableDataView extends View implements GestureDetector
scrollAnimator.cancel(); scrollAnimator.cancel();
} }
} }
public int getDataOffset()
{
return dataOffset;
}
public void setScrollerBucketSize(int scrollerBucketSize)
{
this.scrollerBucketSize = scrollerBucketSize;
}
} }

@ -2,6 +2,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<solid android:color="#30000000" /> <solid android:color="#7f000000" />
<corners android:radius="10dp" /> <corners android:radius="10dp" />
</shape> </shape>

@ -11,7 +11,6 @@
tools:context="org.isoron.uhabits.ShowHabitActivity"> tools:context="org.isoron.uhabits.ShowHabitActivity">
<LinearLayout <LinearLayout
android:id="@+id/llOverview"
style="@style/cardStyle" style="@style/cardStyle"
android:gravity="center"> android:gravity="center">
@ -28,10 +27,7 @@
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout style="@style/cardStyle">
android:id="@+id/llStrength"
style="@style/cardStyle"
android:gravity="center">
<TextView <TextView
android:id="@+id/tvStrength" android:id="@+id/tvStrength"
@ -41,7 +37,7 @@
<org.isoron.uhabits.views.HabitScoreView <org.isoron.uhabits.views.HabitScoreView
android:id="@+id/scoreView" android:id="@+id/scoreView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="180dp"/> android:layout_height="200dp"/>
</LinearLayout> </LinearLayout>
@ -81,7 +77,7 @@
<org.isoron.uhabits.views.HabitStreakView <org.isoron.uhabits.views.HabitStreakView
android:id="@+id/streakView" android:id="@+id/streakView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="180dp"/> android:layout_height="200dp"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
Loading…
Cancel
Save