diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java index f4eaa0cfb..97699246c 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java @@ -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; } } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java index 8a1dc078f..6b4294f80 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java @@ -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 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; - } } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java index 4f07daaf6..cf3fb7569 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java @@ -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 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; - } } diff --git a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java new file mode 100644 index 000000000..5368595a4 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java @@ -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 . + * + * + */ + +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(); + } +}