Listview changing size of (reused) views - android

I am all for reusing views in listview. I always set visibility, contents, witdth etc. of all controls again in getView Unfortunately it seems ListView fails to recalculate height.
Picture one shows the initial item showed:
Picture two shows how item one is rendered after we scrolled away and back into it
The background linearlayout height (the black area) made me think that in picture two, Android is reusing a view that just showed a much heigher item (e.g. the second item). But why does it not recalibrate/reset/recalclulate itself (it is in "wrap_content" mode in its XML) when reused as view for the first item which content (text + image) is not as heigh?
In truth I am not sure what is happening. The problem only manifests itself if I have image in the view. I have tried organize the bitmap/image loading in different ways (sample code underneath) with different things commented out, but that does not seem to make much difference. I am really at a loss here as to the reason.
override_listitem_news.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dip"
android:background="#android:color/black"
>
<TextView
android:id="#+id/listitem_news_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="22sp"
android:padding="5dip"
android:text="#string/newsItemTitle"/>
<TextView
android:id="#+id/listitem_news_date"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="italic"
android:textSize="15sp"
android:padding="5dip"
android:text="#string/newsItemDate"/>
<TextView
android:id="#+id/listitem_news_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textStyle="normal"
android:textSize="15sp"
android:padding="5dip"
android:autoLink="web"
android:text="#string/newsItemDesc"
android:background="#android:color/darker_gray"
/>
<ImageView
android:id="#+id/listitem_news_icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Here is code where I load image in getView
ViewTreeObserver vto = image.getViewTreeObserver();
vto.addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
image.getViewTreeObserver().removeGlobalOnLayoutListener(this);
image.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
SharedCode.sharedUtilScaleImage_Width(image);
}
}
);
image.setTag(data.image_file_name + data.image_file_url);
Bitmap bit = null;
bit = SharedCode.sharedGetFileFromOffline(thisActivityContext, "news", data.image_file_name, MyGetKindOfFile.ImageAsBitmap).bitmap;
if (bit != null) {
image.setImageBitmap(bit);
image.setVisibility(View.VISIBLE);
}
else {
image.setImageBitmap(null);
image.setVisibility(View.GONE);
}
image.setPadding(0, 0, 0, 0);
image.setBackgroundColor(data.backgroundColorInt);

For what it is worth, problem appeared to be related to the imageview. Just for reference, I will write here how I solved it.
In getView I fixed the imageview width to screen width (instead of "wrap-content" and/or parent view width - earlier code used OnGlobalLayoutListener for parent width)
I switched over to using SetDrawable instead of SetImageBitmap. It is odd, but this difference was actual very important in solving the odd space around the imageview after scrolling an item/row in/out of view.
My research did also indicate that others had problems using wrap_content in listview for cases similar to mine, but I was not able to find anyone who had experienced exact same problems as me.

Related

Height of layouts inside of a viewpager whose height is set to wrap content

I ran into the issue concerning a ViewPager's height not being set correctly when setting android:height="wrap_content"
Now, I've successfully applied the fix mentioned here and it is working as expected. My ViewPager is correctly assuming the height of its tallest element.
However! Each of the items inside of my viewpager contains a linearlayout, which contains 2 elements, like so: (This is my container)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:layout_margin="#dimen/gutterSpaceHalf"
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="#+id/foo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
This gives me in effect a layout that kind-of looks like this:
And what I'm trying to illustrate here is that, on the second page, LinearLayout foo will have a different height from the first page. This is expected.
But the problem comes in when I want to align the button on each page to the bottom! This is of course an issue as the bottom of foo on page one will not be the same as the bottom of foo on page two.
The standard solution, in my mind, would be to set the height of foo to match_parent. However, if I do this, the whole page becomes the height of my imageview, as the applied fix mentioned above can not calculate each child's height correctly anymore.
So, bearing in mind that my entire goal is to have equal-height pages, with dynamic content (the text), while keeping the buttons aligned to the bottom of the page, what solution would you suggest here?
Things I've tried include jiggering the height settings on layouts up the tree, and setting the height of each view to the measured height of its container upon being added in my ViewPagerAdapter, but nothing seems to be working how I want it to.
Any suggestions would be highly appreciated. Please comment if I've missed something or you'd like to see more information.
IVE DONE IT YES
All I had to do was add a view with weight=1 above my button, and give my button a static height. Now the button lines up to the bottom correctly on each page, due to the added view expanding to fill remaining size.
I also had to add +1 to the height as defined in the "hacky" solution I mentioned above, else the view I added would push items out of the largest page's layout, like so:
if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + 1 /* This is a heuristic solution */, MeasureSpec.EXACTLY);
}
YESSSSSS
(layout changes):
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View <!-- ADDED -->
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="#dimen/buttonHeight"/>

Offset vertical position of a child of a RelativeLayout which is vertically centered

I have a ViewPager:
<android.support.v4.view.ViewPager android:id="#+id/ft_graph_pager"
android:layout_width="match_parent"
android:layout_height="#dimen/bar_graph_pager_height"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#id/ft_rsvp_graph_indicator"/>
Which is a child of a RelativeLayout. Behind this ViewPager is a 9patch image representing the lines of a graph.
The content of the ViewPager should line up with the 9patch graph lines, but I can't just make them the same height and call it a day because the ViewPager contents have a caption which should be positioned below the graph lines.
With the XML above, this is what it looks like:
I'm super close. As you can see, if I can just get the ViewPager's contents (the bar graph items and San Diego below) to shift down slightly I'm in business.
The first thing I tried to do is set layout_marginBottom="-6dp" which did nothing.
Then I tried to set paddingBottom="-6dp" which also did nothing.
Setting paddingTop="6dp" doesn't do anything either, but setting the bottom padding to a positive number DOES have an affect, even though it is completely unhelpful for me.
The RelativeLayout parent for this Pager and 9patch contains another view as well, one which is larger than both and hinges on the 9patch being perfectly vertically centered, so I can't make the 9patch move instead.
The full contents of the RelativeLayout are below:
<RelativeLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout android:id="#+id/ft_bar_graph_indicator"
android:layout_width="#dimen/bar_graph_indicator_width"
android:layout_height="#dimen/bar_graph_indicator_height"
android:orientation="vertical">
<!--TODO fill-->
</LinearLayout>
<View android:layout_width="match_parent"
android:layout_height="#dimen/bar_graph_height"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#id/ft_bar_graph_indicator"
android:background="#drawable/graph_lines"/>
<android.support.v4.view.ViewPager android:id="#+id/ft_graph_pager"
android:layout_width="match_parent"
android:layout_height="#dimen/bar_graph_pager_height"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#id/ft_rsvp_graph_indicator"/>
</RelativeLayout>
Any ideas?
Well, I was unable to figure out how to do this via XML, so if someone is able to do that go ahead and post it and I will make that the official answer.
However I was able to come up with a work around in Java. When I declared my ViewPager in my Activity, I just added this:
Display display = mActivity.getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
ViewHelper.setTranslationY(mGraphPager, 5 * metrics.density);
5 being the dp I ended up shifting it down. ViewHelper is part of the NineOldAndroids library, which helps simulate methods from the View class added in API 11+

Android GridView Not Scaling Properly

I have spent hours on this and looked at every related SO question I could find without a solution.
Here is my problem:
I have a gridview of images. I need 3 columns (to match the iOS version and aesthetics). If I set the numColumns to 3, I get a lot extra space between the top and bottom of the image row for each row. If I set the width to autofit, I always get 2, they look better but would prefer 3 columns.
What am I missing?
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="top" >
<LinearLayout
android:id="#+id/llayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
>
<ImageView
android:id="#+id/header"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#666666"
android:clickable="false"
android:contentDescription="#string/title_card_collection"
android:src="#drawable/sportscard2x" />
</LinearLayout>
<GridView
android:id="#+id/cardGrid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_above="#+id/llayout"
android:gravity="center"
android:horizontalSpacing="2dip"
android:numColumns="3"
android:stretchMode="columnWidth"
android:layout_alignParentBottom="true"
android:verticalSpacing="2dip" >
</GridView>
I am hoping it is something easy that I am just missing. Thanks in advance.
UPDATED: Added Screenshot. The problem is that only 1 row is showing, if you scroll down, you see the other 3 rows, but there is a huge space in between.
<edit>
Please find "Tip 3" below. Example with code can be found here.
</edit>
I think the problem is that you set the layout_height of your <GridView> to wrap_content. The grid view doesn't seem to be able to calculate it's total, wrapped height properly, hence, it's showing one row only.
You could either set the layout_height to match_parent (or any other, fixed height - like 120dp or whatever) or your could try to extend the GridView Java object and do some own calculation (you would then need to use your custom grid view object in your XML-layout as well: <com.package.to.MyCustomGridView>).
I do need to stress, though, that there aren't any well defined way of getting hold of the number of columns from the grid view in Java code prior to API 11 (you would want this in order to calculate how many rows your data adapter would produce). The getNumColumns was introduced in API 11. Neither is there a proper way of finding the spacing between rows (the getHorizontalSpacing() and getVerticalSpacing() methods were introduced in API 16).
For your reference: http://developer.android.com/reference/android/widget/GridView.html
Tip 1:
I haven't tested this my self, but you could try something like this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
/>
<GridView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
...
/>
</LinearLayout>
I.e. including your grid view in the linear layout and adding all available space to it after the image view has been laid out.
Tip 2:
You could write your own list view. There seems to be some good candy on the Sony Mobile tutorial site (and no, I'm not employed by Sony Mobile :-), nevertheless; a good tutorial is a good tutorial): http://developer.sonymobile.com/2010/05/20/android-tutorial-making-your-own-3d-list-part-1/
Tip 3:
You could extend the Java GridView object and override the onMeasure method as below example. Remember to refer to your extending GridView class in the Android layout XML file (like <com.package.MyGridView android:layout_width="match_parent" android:layout_height="wrap_content" ... />)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSpec;
// The great Android "hackatlon" (in order to enable "wrap_content" on grid views).
if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
heightSpec = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
}
else {
heightSpec = heightMeasureSpec;
}
super.onMeasure(widthMeasureSpec, heightSpec);
}
Hopefully you'll find some inspiration in this :-)
Cheers,
-- dbm

Android Linear Layout gives me headache

I've got the following xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayout1"
android:background="#android:color/transparent"
android:layout_marginTop="0px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:weightSum="1"
android:orientation="horizontal">
<Button android:id="#+id/info" android:text="Info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:id="#+id/town" android:text="Town" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:id="#+id/unit" android:text="Unit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:layout_height="wrap_content" android:text="EndTurn" android:id="#+id/endturn" android:layout_width="wrap_content" android:layout_gravity="bottom"></Button>
</LinearLayout>
which provides the following result: http://i42.tinypic.com/otdkb4.png
Now I've got some questions about this:
The top and bottom padding, how to get rid of it?
I tried RelativeLayout, multiple layouts within each other, padding, margin, changing height nothing seem to affect it in any way.
Is there a way to get the layout transparent? android:background seems to be the wrong one.
Between the third and the fifth button is a bit more space (where the fourth button should be). I catch it the in the program and set it to invisible.
unitButton.setVisibility(INVISIBLE);
unitButton.setWidth(0);
Now the space between the two buttons is more than double of the normal range (between 1 and 2) Any idea on this? - Altough this is a minor problem
Thanks in advance.
1: Is the layout presented in a Dialog? If so, that'll give you some headaches. To get more control you should either create your own custom Dialog extension (as some dialog layout values are hardcoded), or display your layout in another way (a new activity on top, or using a framelayout perhaps)?
2: To get a layout transparent, simply don't give it a background-attribute. (Though, if you really are using a dialog, the dialog box is not transparent, and it is that which you see. You can also set it to be transparent by setting background to "#00000000" (which is what you do).
3: A View with visibility as "invisible" is still measured, that means both its width/height as well as its margins and padding is displayed as empty space in your layout. Setting the visibility to "gone" instead will not measure it, and you won't need the setWidth(0) either. (You can still display it later by setting it back to "visible")
Edit: removing the unused "weightSum" attribute might also be a good idea, as the view is now expecting its children to have a total weight of something other than 0.

Android horizontal LinearLayout with wrapped text in TextView

I've observed a behavior with layout_weight that I can't explain. The following is a trivial example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="This is a very very very very very very very very very very very very very very very long string."
android:layout_weight="1"
/>
<View
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="#ffffffff"
/>
</LinearLayout>
In a QVGA display, the TextView wraps the text. The white square is displayed to the right of the text.
However, if I remove android:layout_weight="1" from the TextView, the TextView now takes up the entire display width. The white square is no longer displayed.
Why would layout_weight in the TextView affect whether or not the white square is displayed? Shouldn't the View with the white background always be assigned 32dpx32dp first? (It makes no difference if the view were any other types - ImageView or TextView).
The problem I was working on is that I want the white square to always be displayed to the right of the TextView (whether or not the text is wrapped), but I don't want any empty space between the TextView and the white square. (If I add android:layout_weight="1" to the TextView, then there is a gap if the text is not wrapped.)
Any help would be appreciated!
To answer my question #1: One thing I learned by looking at the source for LinearLayout: Not only does layout_weight assign unused space to a child, it also shrinks a child with layout_weight if the child extends beyond the bounds of the LinearLayout. That explains why a TextView with wrapped text is shrunk in my layout.
As for the answer to my question #2, I think you meant android:toRigthOf instead of android:layout_alignRight. Using a RelativeLayout instead of a LinearLayout doesn't change the layout behavior. The tricky part is placing a view immediately to the right of a TextView, without gaps, whether or not the text is wrapped. Setting a maxWidth would limit the TextView's width, but that solution doesn't scale across portrait/landscape and different display dimensions.
Solution - Looks like Dyarish's solution is the best available. My layout problem exists regardless of the layout you use. The key is to set a maxWidth for the TextView so that it doesn't take up the all of the horizontal space in the layout. Because hardcoding a android:maxWidth value in the TextView doesn't scale across different displays, setting the maxWidth at runtime, as Dyarish suggested, is a good solution.
Hopefully this is what you are looking for.
First off, here is a great resource I found for Creating UI's.
layout_weight - Specifies how much of the extra space in the layout to be allocated to the View.
If you want to ensure that the white square is always to the right of the textview, you can use a Relative View, and add the parameter to the view. android:layout_alignRight="+id#yourTextViewID". This should always make the box appear right beside the textView area. You should probably also add something like android:maxWidth="250px" This will ensure that you don't push the white box completely out of the screen.
Here is a code sample:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:maxWidth="250px"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32dp" android:layout_height="32dp" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You could also add to the View:
android:layout_alignTop="#+id/TextForWhiteBox" android:layout_alignBottom="#+id/TextForWhiteBox"
to make the white box the same size as the TextView.
Firstly I've tested the code from my other answer and it does exactly what you've described you've wanted. (unless I'm misunderstanding what you are asking for). You definitely do not want to use the android:layout_alignRight which is not what is in the code sample. That would simply keep the box on the right hand of the screen and not be affected by the textview at all. This sample uses android:layout_toRightOf="#+id/TextForWhiteBox" which is possible due to it being a relative layout. Since the Relative Layout allows you to place objects in relation to others. That line will always place the box just to the right of the textview with no gaps.
As for the screen orientation changes:
When the orientation changes it creates a new instance of the view.
Here is a simple solution.
//Add to oncreate in your Activity
private TextView textStatus;
textStatus = (TextView) findViewById(R.id.TextForWhiteBox);
// This get's the width of your display.
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
// Now you know the screen orientation, and it's width. So just set the maxwidth of the text view to match the display width - the pixels of your white box.
textStatus.setMaxWidth(width - 32); // 32 is here because you already know the size of the white box. More logic is needed to dynamically get this value, because you would need to wait for the activity to be fully created.
}
Here is the main.xml I used:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32px" android:layout_height="32px" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You might need some additional logic to keep screen values.
This code has been tested, you should be able to literally copy and paste this to work as you asked.
Also depending on your logic you could use something like this to return the screen orientation.
int orient = getResources().getConfiguration().orientation;
Hope this helps!
If this helped you, please click the accepted button. =) Cheers!

Categories

Resources