I have a problem with spacing in my Android app: Unwanted space between columns.
I noticed similar questions have been asked on stackoverflow before, but none seemed to match exactly my case.
Here's what it looks like (using the code below)... http://i.imgur.com/OXIms9n.png
From my PlayAdapter.java
#Override
public View getView(int index, View view, ViewGroup viewGroup) {
ImageView imageView = new ImageView(context1);
imageView.setImageBitmap(images[index]);
return imageView;
}
From my Spacing.java
GridView gridView = (GridView) findViewById(R.id.gridview2);
final PlayAdapter PlayAdapter1 = new PlayAdapter(this, images);
gridView.setAdapter(PlayAdapter1);
gridView.setNumColumns(numCols);
gridView.setColumnWidth(width);
(numCols and width are determined programmatically elsewhere )
The GridView:
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/gridview2"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:horizontalSpacing="2dp"
android:verticalSpacing="2dp"
android:layout_gravity="center"
android:stretchMode="spacingWidthUniform">
</GridView>
So, if stretchMode is set to "columnWidth", "spacingWidth" or
"spacingWidthUniform" there is too much space between
the columns of the grid. See the imgur link above for the result.
Here's a solution I already tried instead:
If stretchMode is changed to "none", the grid is centered
vertically in portrait mode, but not horizontally in
landscape mode. Looks like this: http://i.imgur.com/Di6TgS2.png
Changing the settings for gravity and
layout_gravity did not help, nor did making a LinearLayout
around the gridview.
Any other ideas what to do?
Related
I am currently having some formatting issues with Android's ListView. After looking through several questions on SO I did not find any proper solutions.
I have a ListView (used for navigation) including one or more views (actual navigation items). What I want to achieve is - the ListView uses up the whole vertical space (heigth set to match_parent) and the cells are centered vertically in this ListView.
Simply setting the gravity to center_vertical for the ListView does not do the trick. I currently can only achieve this look if I wrap the ListView in another view (which has set the gravity to center_vertical) and change the ListView's height to wrap_content. But this seems not to be the perfect solution as the resulting measuring operations (the adapter’s getView method is called multiple times for the same position) have a performance impact, even if applying a proper holder concept for the navigation items. Is there any solution to this issue?
ListView (gravity does not work):
<ListView android:id="#+id/lvNavigation"
android:layout_width="#dimen/navWidth"
android:layout_height="match_parent"
android:gravity="center_vertical" />
ListView with workaround (bad performance):
<LinearLayout android:layout_width="#dimen/navWidth"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_vertical">
<ListView android:id="#+id/lvNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Navigation item:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_weight="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="#dimen/horizontalMargin"
android:paddingRight="#dimen/horizontalMargin">
...
</LinearLayout>
Listview is probably a bad thing to use for this purpose. I think what you should do is use a recycler view with a custom Layout Manager
In particular you probably want to override onLayoutChildren
I did a quick search and could not find a library that does this for you already.
Alternatively, if you have elements where you can predict the height you can add a header view that is blank and set the height so that the list items will appear centered.
So something like
ListView lv = getListView();
LayoutInflater inflater = getLayoutInflater();
ViewGroup header = (ViewGroup)inflater.inflate(R.layout.header, lv, false);
header.setLayoutParams(new AbsListView.LayoutParams(<width>, <height>));
lv.addHeaderView(header, null, false);
I have a FrameLayout that loads Fragments by tapping on tabs in a TabWidget. I can't figure out how to make the height of the FrameLayout as tall as its content, so that the whole containing ScrollView will scroll together as one instead of a separate scrolling view.
Here's a visual example of this Fragment's structure:
As you can see, the Frame Layout Visible Height only reveals one row of the Fragment, when in fact, there are a few. I can scroll within the FrameLayout to see the other rows as it is now, but that's not what I'm going for. The FrameLayout is made up of a LinearLayout containing a GridView with their layout_heights set to wrap_content.
I tried hardcoding the height of the FrameLayout to something like 500dp and it works great except for the fact that it's no longer dynamically sized. Would I need to resize the FrameLayout programmatically each time a new image is loaded into the inner content? Is there a layout attribute I can set so it'll stretch its height to match its inner content?
Here's my layout xml file:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="200dp">
<!-- CONTAINS USER INFO AND STATS -->
</RelativeLayout>
<android.support.v4.app.FragmentTabHost
android:id="#android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<TabWidget
android:id="#android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:weightSum="2">
</TabWidget>
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</FrameLayout>
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
</LinearLayout>
</ScrollView>
Thank you!
Since I'm going to set a bounty on this, I thought I'd share what I've figured out so far.
In the thumbnails, onSuccess when each image is loaded, I'm calling a function in the GridLayout that holds the images that counts the images and sets the height of the GridLayout. This works fine, although it seems like it'd be a bit inefficient.
What I'm doing is setting the GridLayout height and then calling requestLayout and invalidate on it and it's parent(s). This works, but not as the images loading. It'll work if I go to a different tab and return to the thumbnails, oddly enough. Which makes me think I'm not updating at the right time or on the right object.
Anyway, that said. Does anyone know how to make the height of a GridLayout expand to hold its contents (instead of scrolling) so I can scroll the entire page (including the top section)?
I should also add the GridView layout:
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid"
android:stretchMode="columnWidth"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawSelectorOnTop="false"
android:fastScrollAlwaysVisible="false"
android:fastScrollEnabled="false"
android:numColumns="3"
android:choiceMode="none">
</GridView>
I was in a similar situation but I had a ListView instead of a GridView. You are right in the part when you have to set the height dynamically each time you add an item or if you call notifyDataSetChanged().
THIS CODE IS FOR LISTVIEW WITH DIFFERENT HEIGHT FOR EACH ROW
private void setListViewHeightBasedOnChildren(MyQueueAdapter listAdapter) {
int desiredWidth = MeasureSpec.makeMeasureSpec(
mListView.getWidth(), MeasureSpec.AT_MOST);
int totalHeight = 0;
View view = null;
int i;
for (i = 0; i < listAdapter.getCount(); i++) {
view = listAdapter.getView(i, view, mListView);
view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
totalHeight += view.getMeasuredHeight();
}
ViewGroup.LayoutParams params = mListView.getLayoutParams();
params.height = heightList
+ (mListView.getDividerHeight() * (listAdapter
.getCount() + 3));
heightListComplete = params.height;
mListView.setLayoutParams(params);
}
You need to modify this code according to your needs, you don't need the loop as the height of each row is static in your case. If you need more help let me know.
ALTERNATIVE
If you know the height of the view in dp you can easily convert the dp in px and set the height of your gridview according to number of rows.
When using dynamic sizes you'll run into problems once you put match_parent inside a wrap_content thing. One tries to get a small as it's content and the content tries to be as big as it's parent. Neither side will know how to scale properly in the end.
ScrollView is such a thing that falls in this category. It's a scalable window to it's content so it can't be wrap_content and the content can't be match_parent because it's parent is a virtual infinite space.
Change <ScrollView android:layout_height="wrap_content" to match_parent (or a fixed size).
To solve the size of the content
set the root layout (LinearLayout in your case) of your ScrollView to be a fixed size so it's content can be match_parent again.
use wrap_content all the way.
combine the two: wrap_content until a child defines an absolute size, then match_parent inside there.
The wrap_content route will only work if all the elements in the layout from inner to outer most expand properly based on their content. Nothing can rely on parent bounds unless you add some.
Your content looks rather dynamic in size. So it is likely that you need to use some code to manually set sizes based on content. E.g. if those images inside your tab frame are a GridView (essentially ScrollView with grid content again) you'll need to set it's size manually. More than 1 degree of freedom in wrapping dynamically sizing containers isn't solvable automatically.
Parent of your frame layout is linear layout whose height is wrap_content. also, your framelayout's height is wrap_content. change both of them to fill_parent. using match_parent is more preferred now a days insted of fill_parent
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.
I have following code for Gridview (To make the Calendar).
<GridView
android:id="#+id/calendar"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/rl1"
android:layout_below="#+id/ll2"
android:columnWidth="250dp"
android:numColumns="7" >
</GridView>
Grid_Cell is as follows.
<?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="fill_parent"
android:background="#drawable/calendar_button_selector" >
<Button
android:id="#+id/calendar_day_gridcell"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/calendar_button_selector"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#000"
android:textSize="14.47sp" >
</Button>
<TextView
android:id="#+id/num_events_per_day"
style="#style/calendar_event_style"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center" >
</TextView>
I want to set no space between the rows and columns.
Existing view is as following.
Please help me out... Thanks in Advance
.
I will try to give you a snippet, but to be honest, there is no point of using a GridView for your case since all you items are there on the screen anyway. You can create a couple of LinearLayouts in a small loop that will get the result.
I would advice you to set the columnWidth on Runtime according to the screen width.
And your adapter should be fed with the column width and height to set them when inflating child views. And in this case, you need to get rid of numColumns. Remember that using numColumns along with columnWidth makes no sense especially when you want to fill the whole space. If you want to set the numColumns, remove the columnWidth.
SOLUTION:
Here is the outcome:
First, we create our layout. In my case, it is the MainActivity's layout and is called activity_main.xml. (Notice there is no GridView because I'll add that later in code):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="#+id/header"
android:background="#444"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#android:color/white"
android:text="Dynamic Static GridView" />
<TextView
android:id="#+id/footer"
android:gravity="center"
android:background="#444"
android:textColor="#android:color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Shush" />
</RelativeLayout>
Our GridView element's layout is here in the item_grid.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#fff"
android:layout_height="match_parent" >
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
Now the trick is in MainActivity (I have commented some of the code for you to understand):
package com.example.dynamicstaticgridview;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.RelativeLayout;
import android.app.Activity;
import android.graphics.Color;
/**
* #author Sherif elKhatib
*
*/
public class MainActivity extends Activity {
private static String items[]; //these are temporary items :p do not use them
static {
items = new String[7*7];
for(int i=0;i<7*7;i++) {
items[i] = String.valueOf(i);
}
}
int numberOfColumns = 7; //defaulting to 7, you can change it when you know
int numberOfRows = 7; //defaulting to 7, you can change it when you know
GridView mGrid;
ArrayAdapter<String> mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.BELOW, R.id.header);
params.addRule(RelativeLayout.ABOVE, R.id.footer);
mGrid = new GridView(this) {
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(!calculated)
getDimens();
}
};
mGrid.setVerticalSpacing(0);
mGrid.setHorizontalSpacing(0);
mGrid.setStretchMode(GridView.NO_STRETCH);
mGrid.setBackgroundColor(Color.rgb(100, 10, 10));
((RelativeLayout)findViewById(R.id.rootview)).addView(mGrid, params);
}
private int mCellWidth;
private int mCellHeight;
boolean calculated = false;
protected void getDimens() {
calculated = true;
//here you might have some rounding errors
//thats why you see some padding around the GridView
mCellWidth = mGrid.getWidth()/numberOfColumns;
mCellHeight = mGrid.getHeight()/numberOfRows;
mGrid.setColumnWidth(mCellWidth);
mGrid.setNumColumns(numberOfColumns);
mAdapter = new ArrayAdapter<String>(this, R.layout.item_grid, R.id.text, items) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
GridView.LayoutParams params = null;
if(convertView == null) {
params = new GridView.LayoutParams(mCellWidth, mCellHeight);
}
convertView = super.getView(position, convertView, parent);
if(params != null) {
convertView.setLayoutParams(params);
}
return convertView;
}
};
mGrid.setAdapter(mAdapter);
}
}
COMMENTS:
You'd really better find a decent algorithm to choose mCellWidth, mCellHeight, numberOfColumns, and numberOfRows.
I might be wrong but as far as I know the GridView is an AdapterView, which in your case means that you can't dynamically, through xml, format the height of your items in a way so that they always fill the entire parent. That is not the way grid views (and list views and other adapter views) work.
These kind of "scrollable container views" could be seen as microfilm readers that used to exist in "modern libraries" a very long time ago. One needs to realize that the data has really nothing to do with the actual viewer, you just use your viewer (the GridView in your case) with a fix with and height to pan over the underlaying data items which also have their fix widths and heights.
As I see it you're trying to explicitly target the very corner-case where the data just happens to have the same geometric size as your viewer window.
A few tips, though:
You could instead have a look at GridLayout. The GridLayout is introduced in API level 14 (IceCream Sandwich) so you might have to rethink your version support strategy there (if I'm not completely misstaking it should also be included in the v7 support package for backwards compatibility on older platforms, in that case you could simply add the jar to your app to support older API levels).
Yet another tip would be to use a TableLayout with corresponding table rows and what not. The TableLayout has been around from day one, so there is no backward compataibility issues there, but they do tend to become very layout intensive, though. If you are planning to reload your view a lot or scroll between months smoothly, I'm not sure this is the solution for you (due to the amount of deep layout routes).
A third solution would be that you still use the GridView, but you measure the height of it and dynamically set a fix height to your inflated grid view items from your grid view adapter, based on the measured GridView height (In other words: you make sure you enter the above mentioned corner-case).
If you use the GridView you'd need to get rid of the vertical spacing as well. There is a android:stretchMode attribute on the GridView which you could play around with. You could also try different (negative?) dimensions on the android:verticalSpacing attribute.
Good luck with your custom calendar view!
If you want to give a height and width to your gridview put a relative layout outside of the gridview and give android:layout_width and android:layout_height to your relative layout. Then give your gridview's width and height to match_parent. After that if you want to give exact height and width to your gridview cell elements at first you have to know the exact width and height of all cells and you should define the columnWidth according to that, for example for this code
<GridView
android:id="#+id/calendar"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/rl1"
android:layout_below="#+id/ll2"
android:columnWidth="250dp"
android:numColumns="7" >
</GridView>
if you dont have 1000dp layout outside you never get 4 cells in one row in a good view but only gridview will try to determine its own cell width.
for your situation your calendar gridview should be something like this:
<GridView
android:id="#+id/calendar"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/rl1"
android:layout_below="#+id/ll2"
android:columnWidth="50dp"
android:numColumns="7" >
</GridView>
It seems to be a solution for you http://www.anddev.org/how_to_display_gridview_with_gridlines_and_borders-t11099.html
Try setting these two values in XML for your GridView:
android:horizontalSpacing="0dp"
android:verticalSpacing="0dp"
You'll probably want to remove the android:columnWidth field.
I have to show 7 icons on the home screen of my application .To do this i have arranged the icons on the grid view using image view to show images .On emulator it looks absolutely fine but images shrink and look blur ,when i deploy the application on LG Optimus p350 please help me how to show the images clearly of actual size :
The code i tried are :
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:columnWidth="61dp"
android:numColumns="3" android:verticalSpacing="10dp"
android:horizontalSpacing="10dp" android:stretchMode="spacingWidthUniform"
android:gravity="center" android:layout_gravity="center" />
icon size is 61*80
EDIT :
<supports-screens android:smallScreens="true"
android:normalScreens="true" android:largeScreens="true"
android:anyDensity="true"/>
Please see the image and the code On emulator the output is shown like this.I want to show output on device also with this clarity.Please guide
see second screen shot (blur on qvga emulator) and please guide me how to correct it ..
The images don't appear blurred to me. They are just smaller due to higher screen resolution. But the text does look blurred.
So I would suggest to use a combination of ImageView and TextView. To separate the text and image. The text will be scaled appropriately.
You can also provide multiple image sets for different resolutions.
EDIT:
You can create a grid item layout similar to this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="bottom"
android:layout_width="wrap_content"
android:layout_gravity="bottom">
<ImageView android:id="#+id/thumbnailImage"
android:layout_height="70dp"
android:layout_width="fill_parent"
android:gravity="center_horizontal" />
<TextView android:id="#+id/thumnailTitle" android:text="TextView"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="fill_parent"
android:gravity="center_horizontal|bottom" />
</LinearLayout>
Create a custom grid adapter extending BaseAdapter class:
public class MyGridAdapter extends BaseAdapter
{...}
associate the adapter with your grid:
adapter = new MyGridAdapter();
GridView gridView = (GridView) findViewById(R.id.gridView);
gridView.setAdapter(adapter);
Then it is just a matter of filling the items to the grid with the getView adapter method:
public View getView(int position, View convertView, ViewGroup parent)
{
// assuming the grid item layout is named "grid_item"
View view = inflater.inflate(R.layout.grid_item, null);
TextView text = (TextView) view.findViewById(R.id.thumnailTitle);
text.setText("Test");
ImageView thumbnail = (ImageView) view.findViewById(R.id.thumbnailImage);
...
Are you providing multiple resolution resources? Read carefully this article about Supporting Multiple Screens.
I don't think the images shrink, they only look smaller because your phone has a higher density or resolution than the emulator you were testing on.