Android GridView Not Scaling Properly - android

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

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"/>

Android ListView width keeps changing

Firstly, I am new to android so if I have missed something basic I apologise.
I have a page which has two ListViews side by side, both with varying amounts of content. I also have a TextView above the ListViews and another TextView below the listviews. These text view boxes change based on items selected in either of the two ListViews.
These two ListViews sit side by side, taking up half of the screen each, while a Textview sits directly above and directly below, both centred to the page. An image is shown below.
This is the page looking normal on load.
The problem is when I select an item from either list. I have a feeling I am missing some XML properties, but I am not sure which properties or if this is even the case. When an item is selected, let's say from the ListView on the right, the TextView at the bottom is updated with text taken from an array. The ListView also decides to change the width and I am not sure why this is.... I don't want the ListView to change width. I want it to remain taking up half of the page and half of the page only.
This is the page after an item from the right ListView has been selected.
I would also like to keep things in RelativeLayout. I also believe it is only an XML issue and not to do with the adapter or any other code so I will not include that for now. I can include it if required.
Any help would be great. Thanks in advance.
content_titles.xml my activity xml file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TitlesActivity">
<ListView
android:id="#+id/unlocked_titles_list"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:longClickable="true"
android:layout_below="#+id/current_title"
android:layout_alignEnd="#+id/requirements"
android:layout_marginEnd="26dp"
android:layout_above="#+id/requirements">
</ListView>
<TextView
android:id="#+id/current_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="65dp"
android:text="Current Title: Novice"
android:textSize="24sp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"/>
<ListView
android:id="#+id/locked_titles_list"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignStart="#+id/requirements"
android:layout_marginStart="28dp"
android:layout_above="#+id/requirements"
android:layout_below="#+id/current_title"/>
<TextView
android:id="#+id/requirements"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="34dp"
android:text="temp"
android:textAlignment="center"
android:textSize="24sp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
activity_listview.xml used as the individual rows of the ListViews
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/label"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:textSize="16dip"
android:textStyle="bold" >
</TextView>
The problems with layout could be caused by ScrollView to be the wrapper
I stumbled upon some note in http://developer.android.com/reference/android/widget/ExpandableListView.html
"...Note: You cannot use the value wrap_content for the android:layout_height attribute of a ExpandableListView in XML if the parent's size is also not strictly specified (for example, if the parent were ScrollView you could not specify wrap_content since it also can be any length. However, you can use wrap_content if the ExpandableListView parent has a specific size, such as 100 pixels."
I removed wrapping ScrollView and linear layout started working properly. Now its only to understand how to wrap the stuff to ScrollView. God help me
But anyway this is really weird behavior. I think that fill_parent is not really correct wording. When using heirarchyviewer tool I always see WRAP_CONTENT and MATCH_PARENT values for layout_width and leayout_height. So probably fill_parent is actually means match_parent which puts me in cognitive dissonance.
You have your layout_width properties set to wrap_content. This means that they could change as the data changes. I would recommend putting your ListViews in a LinearLayout with orientation:horizontal and set the amount of space that each element takes up with layout_weight. Here is a relevant SO question What does android:layout_weight mean?

text wrapping in two lines - recyclerview

i am designing the app having expected UI like this
i am using RecyclerView with
StaggeredGridLayoutManager manager=new StaggeredGridLayoutManager(3,1);
recyclerView.setLayoutManager(manager);
and this is the row xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/padding_8dp"
android:layout_marginLeft="#dimen/padding_4dp"
android:layout_marginRight="#dimen/padding_4dp"
>
<com.skcsllp.mutterfly.views.widgets.MfTextView
android:id="#+id/tagName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/textview"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:textSize="#dimen/text_18"
android:background="#drawable/selector_tags"
android:textColor="#color/tags_text_color"
/>
</LinearLayout>
using this much i am getting the output as
i don't want tags to go into second line. i am aware about the fact that i have used the span count as 3 and so getting 3 tags in one line but is there any way to avoid the text wrapping in two lines?
Instead of RecyclerView i would suggest you to use AndroidTagView
need to add dependency
dependencies {
compile 'co.lujun:androidtagview:1.0.3'
}
in your main xml, instead of RecyclerView
<co.lujun.androidtagview.TagContainerLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:padding="10dp"
app:container_enable_drag="false"
app:horizontal_interval="10dp"
app:vertical_interval="10dp"
app:tag_clickable="true"
app:tag_theme="pure_teal" />
and in your activity/fragment
TagContainerLayout mTagContainerLayout = (TagContainerLayout) findViewById(R.id.tagcontainerLayout);
mTagContainerLayout.setTags(List<String> tags);
you will get output something like below, for more you can refer that library, they have given nice example project also.
other usefull libraries are listed below :
https://github.com/loopeer/MultiTextTagView
https://github.com/klinker41/android-chips
https://github.com/ApmeM/android-flowlayout
https://github.com/blazsolar/FlowLayout
I think , you have to write a custom ViewGroup. Using the ViewGroup's onMeasure and onLayout, Get the total width of the screen. Then measure the size of TextView. If the TextView width is less than the screen width, place the TextView. Continue to next TextView, with remaining width. If the TextView width is greater than the remaining screen width, then place the TextView at the next line. Continue this until you placed all the TextViews.

Setting "z-index" on LinearLayout on android

I am placing four image views on a vertical linear layout. I want them to ocuppy the same space, so I assign to each an android:layout_weight="1". I also want them to overlap (that is a design requeriment), so I set a negative margin for each view. The last image I add (#+id/floor_1st) is the last to be added (the one at the bottom), so it stays at the front. However, I want it to be the other way around: I want the first image on layout to be at the front followed by the second and so on (the last image shuld be at the back).
I understand that it is easier to control the order the images are placed using a RelativeLayout, but I do not know how to place the images the way I want using this layout. I have also seen that is possible to use the method bringToFront(), but that just do not let the images to overlap.
So, is there any way to place the images in the order I want using LinearLayout? Or should I use another layout? In this case, how should I place the images?
Here is my xml code
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/floors_container"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/overview_buttons_top_margin"
android:layout_marginBottom="#dimen/overview_buttons_bottom_margin"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floor_4th"
android:src="#drawable/piso_4"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="#dimen/floors_overview_margin_three_quarter"
android:clickable="true" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floor_3rd"
android:src="#drawable/piso_3"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:layout_marginTop="#dimen/floors_overview_margin_quarter"
android:layout_marginBottom="#dimen/floors_overview_margin_half"
android:clickable="true" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floor_2nd"
android:layout_gravity="center_horizontal"
android:src="#drawable/piso_2"
android:layout_weight="1"
android:layout_marginTop="#dimen/floors_overview_margin_half"
android:layout_marginBottom="#dimen/floors_overview_margin_quarter"
android:clickable="true" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/floor_1st"
android:src="#drawable/piso_1"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
android:layout_marginTop="#dimen/floors_overview_margin_three_quarter"
android:clickable="true" />
</LinearLayout>
Thanks.
If you want to reverse drawing order, you need to subclass the LinearLayout class and override getChildDrawingOrder.
#Override
protected int getChildDrawingOrder(int childCount, int i) {
//The standard implementation just retuns i
return childCount - i - 1;
}
Make sure to enable custom ordering somewhere:
setChildrenDrawingOrderEnabled(true);
For Android from Level 21, you can use view.setZ() http://developer.android.com/reference/android/view/View.html#Drawing
For Android level below 21, I suggest to use either FrameLayout or RelativeLayout combine with bringToFront() and/or negative padding, margin if required. For using of bringToFront() method, refer to this Defining Z order of views of RelativeLayout in Android
For achieving this kind of layout FrameLayout would be your best bet.
This layout is generally used for z-Index based structure(overlapping). Take a look about this class here :- FrameLayout .
And here is one link which shows its use :- Example Given.
You can find other links too demonstrating its use.
Hope it helps,
Thanks.

Listview changing size of (reused) views

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.

Categories

Resources