I have a simple problem. I'm extending ViewGroup, and I want to align buttons to the right side of the screen from top-to-bottom. Problem is, nothing shows up on my screen. I've confirmed it's not a problem with anything else, only my onLayout() overriden method. Could you help me out?
Code in question:
final int count = getChildCount();
int curWidth, curHeight, curLeft, curTop;
//get the available size of child view
int childLeft = this.getPaddingLeft();
int childTop = this.getPaddingTop();
int childRight = this.getMeasuredWidth() - this.getPaddingRight();
int childBottom = this.getMeasuredHeight() - this.getPaddingBottom();
int childWidth = childRight - childLeft;
int childHeight = childBottom - childTop;
curTop = childTop;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
//Get the maximum size of the child
child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
curWidth = child.getMeasuredWidth();
curHeight = child.getMeasuredHeight();
child.layout(getMeasuredWidth() - getPaddingRight() - curWidth,
curTop, getMeasuredWidth() - getPaddingRight(), curTop - curHeight);
curTop -= childHeight;
}
I've added a few LOG statements to my code, and what I have is frankly infuriating.
07-11 14:46:46.321 32172-32172/milespeele.canvas D/Miles﹕ LEFT: 912
07-11 14:46:46.322 32172-32172/milespeele.canvas D/Miles﹕ TOP: 1008
07-11 14:46:46.322 32172-32172/milespeele.canvas D/Miles﹕ RIGHT: 1080
07-11 14:46:46.322 32172-32172/milespeele.canvas D/Miles﹕ BOTTOM: 1008
These are all valid coordinates (for one button) given the dimensions of my phone's creen, but no buttons are appearing.
I didn't scan this completely, but I see you have curTop - curHeight as last parameter in child.layout(). Should it be curTop + curHeight?
You are decrementing curTop. Coordinate values increase going down the screen, so you are actually layout out children off screen above the top of the ViewGroup. I think you want curTop + curHeight as the last argument of child.layout(), and i think the last line should be curTop += curHeight.
As an aside, you really should not be measuring children in onLayout(). That's what onMeasure() is for.
Solved. Thanks for those that answered.
It seemed the problem was I misunderstood android's coordinate system.
Working code: (where l & r are parameters of onLayout())
final int count = getChildCount();
int left = (int) (l + getMeasuredWidth() - buttonMargin - buttonWidth);
int right = (int) (r - buttonMargin);
int currentTop = (int) (t + buttonMargin);
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
child.layout(left, currentTop, right, currentTop + buttonHeight);
currentTop += buttonHeight + buttonMargin;
}
Related
I'm trying to draw 3 rows of barrels with 4 barrels each row. In order for it to fit all screens I'm using canvas' height and width. this is my code:
int width = canvas.getWidth();
int height=canvas.getHeight();
int x = 20, y = 20, count = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
canvas.drawBitmap(BarrelSprite, width*(x/100), height*(y/100), null);
rect[count] = new Rect( width*(x/100), height*(y/100), width*(x/100) + BarrelSprite.getWidth(), height*(y/100) + BarrelSprite.getHeight());
count++;
x += 20;
}
y += 20;
x = 20;
when I run it all the barrels seems to stack at the top left corner for some reason. I wanted to draw every barrel's X in the first 20% of the screen, 40%,60% etc. same for Y value.
found the mistake, height was int variable and dividing it by 100 making it 0 so I multiply by 0 all the the time hence making all the barrels fit the 0,0 coordinates
Can we get the visible width of a child inside a horizontalscrollview ?
i tried
int viewLeft = v.getLeft();
int viewRight = v.getRight();
but it's always the same result
I need to know if the visibility is higher than half the width
"Visible width" sounds like you want a child view's width actually visible to the user (whole width minus the currently invisible part(s)).
In that case it's correct to use getLeft() and getRight(), but like this instead (hsv being your HorizontalScrollView instance):
int viewLeft = v.getLeft();
int viewRight = v.getRight();
int scrollX = hsv.getScrollX();
if (scrollX > viewLeft) {
viewLeft = scrollX;
}
if (scrollX < viewRight) {
viewRight = scrollX + hsv.getWidth();
}
int visibleWidth = viewRight - viewLeft;
Hi I'm trying to make a square 8x8 grid on a canvas. I've managed to make a grid, but it turns out to be rectangular, but for the game I'm making it needs to be square. How do I change my code to make it a square grid scaled to the phone.
float testWidth = (getWidth() - 16f) / 9f;
float testHeight = (getHeight() - 16f) / 9f;
for (int i = 0; i < 9; i++) {
canvas.drawLine(padding + testWidth* i, padding, padding
+ testWidth * i, testHeight* 8+padding, dark);
canvas.drawLine(padding, padding+testHeight* i, testWidth* 8
+ padding, padding+testHeight* i, dark);
}
EDIT: I can now make a square grid, but I don't know how to center the grid into the middle of the phone
You'll want to take the shortest of the two (Width or Height) and use that to build the grid upon. (So your grid can fit on the screen)
Something like...:
float gridSide = 0;
if (getWidth() > getHeight()) {
gridSide = getHeight();
}
else {
gridSide = getWidth();
}
Simpler logic provided by appsroxcom:
float gridSide = Math.min(testWidth(), testHeight());
Use gridSide as the total length and total width of the grid
I have recently delved into creating custom ViewGroups and have encountered a problem I can't figure out.
I have 2 ViewGroups - ViewManager and Article
ViewManager simply lays out an Article below the previous Article (ie like a vertical LinearLayout)
Article arranges several TextViews and an ImageView as you can see in the image. Creating a single Article and adding it to ViewManager works fine and everything shows up but when I add a second Article none of the content of the Article is visible.
So why are none of the TextViews or the ImageView showing up (note the blue bar is drawn with canvas.drawRect() in onDraw()). I have also printed out (via logcat) the the bound values of the child views(ie getLeft()/Top()/Right()/Bottom() in drawChild() and they all seem to look fine
FIRST TextView
FIRST left 5 right 225 top 26 bottom 147
FIRST TextView
FIRST left 5 right 320 top 147 bottom 198
FIRST TextView
FIRST left 5 right 180 top 208 bottom 222
FIRST ImageView
FIRST left 225 right 315 top 34 bottom 129
SECOND TextView
SECOND left 10 right 53 top 238 bottom 257
SECOND TextView
SECOND left 5 right 325 top 257 bottom 349
SECOND TextView
SECOND left 5 right 320 top 349 bottom 400
SECOND TextView
SECOND left 5 right 180 top 410 bottom 424
So does anyone have an idea of what I've done wrong?
the Article onMeasure method
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
specWidth = widthSpecSize;
int totalHeight=0;
int width = 0;
if(mImage!=null&&mImage.getParent()!=null){
measureChild(mImage,MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(100, MeasureSpec.AT_MOST));
width=widthSpecSize-mImage.getWidth();
imageWidth = mImage.getMeasuredWidth();
}
for(int i = 0;i<this.getChildCount();i++){
final View child = this.getChildAt(i);
//get the width of the available view minus the image width
if(imageWidth > 0)
width =widthSpecSize- imageWidth;
else
width = widthSpecSize;
//measure only the textviews
if(!(child instanceof ImageView)){
measureChild(child, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), heightMeasureSpec);
//calculate total height of the views, used to set the dimension
totalHeight+=child.getMeasuredHeight();
}
}
//if the title height is greater than the image then the snippet
//can stretch to full width
if(mTitle.getMeasuredHeight()>mImage.getMeasuredHeight())
measureChild(mSnippet, MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.AT_MOST), heightMeasureSpec);
//measure source to make it full width
measureChild(mSource,MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST), heightMeasureSpec);
setMeasuredDimension(widthSpecSize, totalHeight);
}
and the onLayout method
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int newLeft = left+outerMargin;
int newTop = top+marginTop;
int prevHeight = 0;
if(mEngine!=null){
int height = mEngine.getMeasuredHeight();
int childRight = newLeft+mEngine.getMeasuredWidth();
mEngine.layout(newLeft+marginLeft, newTop+prevHeight+2, childRight+marginLeft, newTop+height+prevHeight+2);
maxRight = Math.max(maxRight, childRight);
prevHeight += height+2;
topBarHeight = mEngine.getMeasuredHeight()+(marginLeft*2);
maxBottom = Math.max(maxBottom, mEngine.getBottom());
}
if(mTitle!=null){
int height = mTitle.getMeasuredHeight();
int childRight = newLeft+mTitle.getMeasuredWidth();
mTitle.layout(newLeft, newTop+prevHeight, childRight, newTop+height+prevHeight);
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mTitle.getBottom());
}
if(mSnippet!=null){
int height = mSnippet.getMeasuredHeight();
int childRight = newLeft+mSnippet.getMeasuredWidth();
mSnippet.layout(newLeft, newTop+prevHeight, right, newTop+height+prevHeight);
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mSnippet.getBottom());
}
if(mSource !=null){
int height = mSource.getMeasuredHeight();
int childRight = newLeft+mSource.getMeasuredWidth();
mSource.layout(newLeft, newTop+prevHeight+(marginTop*2), childRight, newTop+height+prevHeight+(marginTop*2));
maxRight = Math.max(maxRight, childRight);
prevHeight += height;
maxBottom = Math.max(maxBottom, mSource.getBottom());
}
if(mImage!=null){
int height = mImage.getMeasuredHeight();
log("mxW "+maxRight);
int childRight = maxRight+mImage.getMeasuredWidth();
mImage.layout(right-mImage.getMeasuredWidth()+5, newTop+topBarHeight, right-5, height+topBarHeight);
totalWidth = childRight;
maxBottom = Math.max(maxBottom, mImage.getBottom());
}else
totalWidth = maxRight;
}
On a side note is it okay to use LayoutParams.WRAP_CONTENT as a parameter for makeMeasureSpec() like this?
MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.AT_MOST)
In onLayout, the children should be positioned with respect to their parent. You are adding top (and left) to the coordinates of the children. That's not a problem for the first Article because top and left are zero. For the second Article, left is still zero but top is the top of the second article in its ViewManager. Just get rid of newTop and newLeft and use, respectively, marginTop and outerMargin in their place.
Regarding your aside: the first argument to makeMeasureSpec is supposed to be a dimension. By using WRAP_CONTENT, you are setting the maximum dimension to -2, which is probably not what you want to do.
I have a non-fullscreen activity (the system notification bar is visible). In order to create my view hierarchy I need to know the size of the the piece of screen that my activity occupates (that is the size of the display detracted by the size of the system notification bar). How can I determine that within the onCreate method?
This is not known in onCreate(). What you should do is participate correctly in the view hierarchy layout process. You do NOT do your layout in onCreate(), you do it in the view hierarchy with the layout managers. If you have some special layout that you can't implement with the standard layout managers, it is pretty easy to write your own -- just implement a ViewGroup subclasses that does the appropriate things in onMeasure() and onLayout().
This is the only correct way to do this because if the available display size changes, your onCreate() will not run again but the view hierarchy will go through its layout process to determine the correct new place to position its views. There are an arbitrary number of reasons why the screen size could change on you like this -- for example on the Xoom tablet when it is plugged in to an HDMI output it makes the system bar larger so that when it mirrors its display to a 720p screen the bottom of applications do not get chopped off.
For example, here's a layout manager that implements a simple version of FrameLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
int childRight = getPaddingLeft()
+ child.getMeasuredWidth() - getPaddingRight();
int childBottom = getPaddingTop()
+ child.getMeasuredHeight() - getPaddingBottom();
child.layout(getPaddingLeft(), getPaddingTop(), childRight, childBottom);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
int measuredChildState = 0;
// Find rightmost and bottom-most child
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
measuredChildState = combineMeasuredStates(measuredChildState,
child.getMeasuredState());
}
}
// Account for padding too
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop + mPaddingBottom();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth,
widthMeasureSpec, measuredChildState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
measuredChildState<<MEASURED_HEIGHT_STATE_SHIFT));
}
Note the last line there is the best way to implement measurement starting with API 11, since it allows you to propagate states like "layout does not fit" up which can be used to do things like determine the size that dialogs need to be. You likely don't need to worry about such things, in which case you can simplify it to a form that works on all versions of the platform:
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
resolveSize(maxHeight, heightMeasureSpec));
There is also an API demo for a slightly more complicated layout:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/animation/FixedGridLayout.html
I can't test it now, but I believe
int h = getWindow().getAttributes().height;
int w = getWindow().getAttributes().width;
API docs:
http://developer.android.com/reference/android/app/Activity.html#getWindow%28%29
http://developer.android.com/reference/android/view/Window.html#getAttributes%28%29
http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html
http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html#height