Tuesday, 17 September 2013

Dynamically Drawing Multiple Child Views to a Subclass of ViewGroup

Dynamically Drawing Multiple Child Views to a Subclass of ViewGroup

OK, this one has really got me stumped. I have scoured every source I
could uncover (including every Stack Overflow question I could find) for
the last five hours, but I am still WAT.
I have a class that extends ViewGroup. I am attempting to place
YouTubeThumbnails onto this ViewGroup in an interesting grid pattern
(that's what the logic in the onThumbnailLoaded method does). However,
only the first thumbnail is drawn to the screen.
A FunkThumbnail is just a wrapper for a YouTubeThumbnailView with some
video metadata thrown in. The log calls at the end of onLayout do prove
that data for multiple thumbnails is being passed to view.layout. I tried
to use the invalidate method and also tried to extend onMeasure according
to some examples but to no avail. I have, however, been able to use this
same code to draw the grid of thumbnails by extending RelativeLayout (and
not overriding onLayout), but the relativeLayout cannot extend past the
edge of the screen...
The reason I am attempting to extend ViewGroup with a custom layout is
because I want my layout to be drawn past the edge of the screen so when
users pan the layout around with swipe movements what's coming onto the
screen is already drawn. I have looked into combining a vertical and
horizontal ScrollLayout, but I think it might end up being more difficult
to implement some custom swipe animations with that method.
Thanks for any help!!! Code follows:
` package com.cagrabowski.nfg;
import java.util.HashSet; import java.util.Iterator;
import android.content.Context; import android.util.AttributeSet; import
android.util.DisplayMetrics; import android.view.View; import
android.view.ViewGroup;
import com.cagrabowski.L; import
com.cagrabowski.nfg.FunkThumbnailFactory.FunkThumbnail; import
com.cagrabowski.nfg.FunkThumbnailFactory.LargeFunkThumbnail; import
com.cagrabowski.nfg.FunkThumbnailFactory.MediumFunkThumbnail; import
com.cagrabowski.nfg.FunkThumbnailFactory.SmallFunkThumbnail;
public class NeurofunkGrid extends ViewGroup implements
FunkThumbnailFactoryClient {
public static final int MAX_DESIRED_ROWS_ON_SCREEN = 4;
public static final int NUM_ROWS_OFF_SCREEN = 6;
public static final int NUM_COLUMNS_OFF_SCREEN = 6;
// PlayerView cannot be less than 100px.
public static final int PLAYER_VIEW_MIN_HEIGHT = 110;
// YouTube thumbnails have 16/9 aspect ratio.
public static final double THUMB_ASPECT_RATIO = 16 / 9d;
private HashSet<FunkThumbnail> nails = new HashSet<FunkThumbnail>();
private boolean[][] grid;
private byte numRows, numColumns;
private short heightUnit, widthUnit;
private LayoutParams smallLP, mediumLP, largeLP, fillLP;
public NeurofunkGrid(Context context) {
super(context);
}
public NeurofunkGrid(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NeurofunkGrid(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
void initialize(FunkActivity funkActivity) {
DisplayMetrics displayMetrics = funkActivity.getResources()
.getDisplayMetrics();
numRows = MAX_DESIRED_ROWS_ON_SCREEN + 1;
while (PLAYER_VIEW_MIN_HEIGHT * --numRows + 1 >
displayMetrics.heightPixels)
;
numColumns = 0;
while (PLAYER_VIEW_MIN_HEIGHT * THUMB_ASPECT_RATIO * ++numColumns <
displayMetrics.widthPixels)
;
heightUnit = (short) (displayMetrics.heightPixels / numRows);
widthUnit = (short) (heightUnit * THUMB_ASPECT_RATIO);
L.e("height unit: " + heightUnit + ", width unit: " + widthUnit);
smallLP = new LayoutParams(widthUnit, heightUnit);
mediumLP = new LayoutParams(widthUnit * 2, heightUnit * 2);
largeLP = new LayoutParams(widthUnit * 3, heightUnit * 3);
fillLP = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
numRows += NUM_ROWS_OFF_SCREEN;
numColumns += NUM_COLUMNS_OFF_SCREEN;
grid = new boolean[numRows][numColumns];
FunkThumbnailFactory.registerFunkThumbnailFactoryClient(this,
funkActivity, funkActivity.getVideoData());
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Iterator<FunkThumbnail> iterator = nails.iterator();
while (iterator.hasNext()) {
FunkThumbnail nail = iterator.next();
int width = 0, height = 0;
switch (nail.getSize()) {
case SMALL:
width = widthUnit;
height = heightUnit;
break;
case MEDIUM:
width = widthUnit * 2;
height = heightUnit * 2;
break;
case LARGE:
width = widthUnit * 3;
height = heightUnit * 3;
break;
}
View view = nail.getView();
float left = view.getX(),
//
top = view.getY(),
//
right = view.getX() + width,
//
bottom = view.getY() + height;
L.e("fleft: " + view.getX() + ", top: " + view.getY() + ", right: "
+ (view.getX() + width) + ", bottom: "
+ (view.getY() + height));
L.e("left: " + (int) left + ", top: " + (int) top + ", right: "
+ (int) right + ", bottom: " + (int) bottom);
view.layout((int) left, (int) top, (int) right, (int) bottom);
}
L.e("onLayout");
/*
* final int count = getChildCount();
*
* for (int i = 0; i < count; i++) { final View child = getChildAt(i);
*
* int left = (int) child.getX(), // top = (int) child.getY(), // right
* = (int) child.getX() + child.getMeasuredWidth(), // bottom = (int)
* child.getY() + child.getMeasuredHeight();
*
* L.e("left: " + left + ", top: " + top + ", right: " + right +
* ", bottom: " + bottom); child.layout(left, top, right, bottom); }
*/
}
@Override
public void onThumbnailLoaded(FunkThumbnail thumbnail) {
/*
* String out = ""; for (int i = 0; i < grid.length; i++) { for (int ii
* = 0; ii < grid[i].length; ii++) { out = out.concat(i + ", " + ii +
* ": " + grid[i][ii] + ", "); } }
*
* L.e(out);
*/
// small thumbnail placement logic
if (thumbnail instanceof SmallFunkThumbnail) {
small: for (int i = 0; i < grid.length; i++) {
int ii = 0;
do {
if (grid[i][ii] == false) {
thumbnail.getView().setX(widthUnit * i);
thumbnail.getView().setY(heightUnit * ii);
nails.add(thumbnail);
addView(thumbnail.getView(), fillLP);
grid[i][ii] = true;
break small;
} else if (ii == grid[i].length - 1) {
break;
}
} while (grid[i][ii++] == true);
if (i == grid.length - 1) {
break small;
}
}
// medium thumbnail placement logic
} else if (thumbnail instanceof MediumFunkThumbnail) {
medium: for (int i = 0; i < grid.length; i++) {
int ii = 0;
do {
if (i + 1 < grid.length && ii + 1 < grid[i].length
&& grid[i][ii] == false && grid[i + 1][ii] == false
&& grid[i][ii + 1] == false
&& grid[i + 1][i + 1] == false) {
thumbnail.getView().setX(widthUnit * i);
thumbnail.getView().setY(heightUnit * ii);
nails.add(thumbnail);
addView(thumbnail.getView(), fillLP);
grid[i][ii] = true;
grid[i + 1][ii] = true;
grid[i][ii + 1] = true;
grid[i + 1][ii + 1] = true;
break medium;
} else if (ii == grid[i].length - 1) {
break;
}
} while (grid[i][ii++] == true);
if (i == grid.length - 1) {
break medium;
}
}
// large thumbnail placement logic
} else if (thumbnail instanceof LargeFunkThumbnail) {
large: for (int i = 0; i < grid.length; i++) {
int ii = 0;
do {
if (i + 2 < grid.length && ii + 2 < grid[i].length
&& grid[i][ii] == false) {
thumbnail.getView().setX(widthUnit * i);
thumbnail.getView().setY(heightUnit * ii);
nails.add(thumbnail);
addView(thumbnail.getView(), fillLP);
grid[i][ii] = true;
grid[i + 1][ii] = true;
grid[i + 2][ii] = true;
grid[i][ii + 1] = true;
grid[i + 1][ii + 1] = true;
grid[i + 2][ii + 1] = true;
grid[i][ii + 2] = true;
grid[i + 1][ii + 2] = true;
grid[i + 2][ii + 2] = true;
break large;
} else if (ii == grid[i].length - 1) {
break;
}
} while (grid[i][ii++] == true);
if (i == grid.length - 1) {
break large;
}
}
} else {
throw new IllegalArgumentException(
"FunkThumbnail must be one of SmallFunkThumbnail,
MediumFunkThumbnail, or LargeFunkThumbnail");
}
L.e("size: " + thumbnail.getSize().toString());
L.e("onThumbnailLoaded");
}
@Override
public byte getNumRows() {
return numRows;
}
@Override
public byte getNumColumns() {
return numColumns;
}
public short getHeightUnit() {
return heightUnit;
}
public short getWidthUnit() {
return widthUnit;
}
} `

No comments:

Post a Comment