I've done all of the research on the matter. I know that Google thinks it's pointless and that the developers, know that it's not. I also know that there is no known workaround, but I know that I am close to making one. The user DougW posted this code:
public class Utility {
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
}
Which almost gets the job done for me. But when I try it, I get a NullPointer exception at the listItem.measure(0, 0) line. The listItem itself is initialized, but the method throws the exception anyway. Please tell me how I can fix this.
Here is my code:
public class ExpenseReportsActivity extends Activity {
private ListView lvReports;
private ExpenseReportListAdapter adapter;
private Button btnSend;
private Button btnCancel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.expensereports);
lvReports = (ListView)findViewById(R.id.lv_reports);
lvReports.setBackgroundResource(R.drawable.shape_expense_report_list);
ColorDrawable cd = new ColorDrawable(0xFFffffff);
lvReports.setDivider(cd);
lvReports.setDividerHeight(1);
adapter = new ExpenseReportListAdapter(this);
lvReports.setAdapter(adapter);
int totalHeight = 0;
for (int i = 0; i < adapter.getCount(); i++) {
View listItem = adapter.getView(i, null, lvReports);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = lvReports.getLayoutParams();
params.height = totalHeight + (lvReports.getDividerHeight() * (adapter.getCount() - 1));
lvReports.setLayoutParams(params);
}
}
Another workaround I am working on is using my custom view's onWindowFocusChanged method. It tells the exacts height of the view. The problem is that the event isn't fired while I am still in my Activiy's onCreate method, nor in my Activity's onWindowFocusChanged method. I tried a custom event, but it never fired (it was placed inside my custom view's onWindowFocusChanged method and the listener was in my Activity's onWindowFocusChanged method).
Ok, as far as I got your needs I think you may just use the ListView.addFooterView(View v) method:
http://developer.android.com/reference/android/widget/ListView.html#addFooterView(android.view.View)
It will allow you to have all your list items + "a few buttons" footer to be scrolled as a single block.
So the code should be smth like that:
import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
public class YourActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LayoutInflater factory = getLayoutInflater();
LinearLayout footer =
(LinearLayout) factory.inflate(R.layout.your_a_few_buttons_footer, null);
getListView().addFooterView(footer);
String[] array = new String[50];
for (int i = 0; i < 50;) { array[i] = "LoremIpsum " + (++i); }
setListAdapter(
new ArrayAdapter<String>(this, R.layout.list_item, array)
);
}
}
Note, the doc says addFooterView() should be called BEFORE the setListAdapter().
UPDATE: to add a View at the top of the list use ListView.addHeaderView(View v). Note that, for instance, LinearLayout is also a View. So you can put anything you want as a header or a footer and it'll be scrolled with the list as an indivisible block.
Out of curiosity, is your layout using RelativeLayout? If so, calling measure(0,0) will always throw an NPE, but a LinearLayout will not.
http://groups.google.com/group/android-developers/browse_thread/thread/5a947482d7dcb605
Change it to Linear and you can make that call. I hope that helps!
I have a situation in my app where I have paragraphs of text, imageviews, all sorts of information on a given subject...and then, depending on the item, there is possibly a ListView of comparison data in the middle of all of that info. About one in every 10 items has it, nestled between all the text. The comparison data is never more than 4 items at max, so I don't want the ListView to scroll, ever. I just want the ListView to appear in its entirety at the exact point I specify.
Adding them all as nested Linear Layouts is insane, so is using MergeAdapter to put all of that together when I may not even have a ListView on screen. And using complex ListView headers & footers is out of the question as well.
I'm not the first person to want that kind of functionality, and I won't be the last. The above solution is nearly perfect, it sizes my ListView so that it's full on screen, and all the scrolling comes from the ScrollView parent. (It's easy as sin to do on the iOS SDK, btw., and a lot of apps over there do similar things; we'll need a good solution for this.)
Related
I am currently developing an Android app based on an iOS version, and I have to implement the following layout for the main menu:
a top hub with a few buttons, and then a ViewPager under it. This ViewPager has a bar over it (between itself and the hub) to display pages' titles. Inside the ViewPager is a ListView to display articles.
The expected scrolling behaviour is the following: When you scroll down from the very top, it should scroll normally and hide the hub, and then when you have scrolled enough to where the title bar reaches the top it should snap the title bar in place and you should still be able to scroll the ListView.
I have no idea how to add the hub to all of this and have the desired behaviour. Everything is already implemented and functions properly without the hub. But I have no idea how to add it and make it work.
I tried putting everything in a ScrollView, but this does not seem to work. The scrolling still works on the ListView of articles but it does not scroll the hub out of the way.
According to most people, you should never have a scrolling element inside another one. But I don't see any other way to do this.
I had multiple listviews inside a scrollview and to make it work I set the height of every list view using the following method. Things used to work fine.
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null)
return;
int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(),
MeasureSpec.UNSPECIFIED);
int totalHeight = 0;
View view = null;
for (int i = 0; i < listAdapter.getCount(); i++) {
view = listAdapter.getView(i, view, listView);
if (i == 0)
view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth,
LayoutParams.WRAP_CONTENT));
view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
totalHeight += view.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
listView.requestLayout();
}
I've implemented this component what works as a ListView, so you don't need to adapt your external code and it solves the problem with ListView inside Scrollview together with other elements...I named it StretchedListView, No external hacks required. It is in an answer to another post: https://stackoverflow.com/a/21878703
Enjoy!
I'm aware that there's been a lot Q&A on this topic, but i still didn't find the right answer.
I want my ListView (inside of a ScrollView) to 'expand' over the bottom edge of the screen to show all of the items without need for scrollbar therefore it wouldn't be a problem to put ListView inside ScrollView and we would be able to scroll whole activity.
Puting items collection in LinearLayout is not a solution - it's just to damn slow. It takes 2-3 sec to draw 100 items while ListView does it instantly.
You could set the ListView's layout_height to wrap_content, but then it would be as slow as the LinearLayout. The whole point of ListView is that its content is fake; it only creates the rows that are visible within its height. Making it as tall as its content would take away its advantage.
I'd suggest re-thinking what you're trying to do. Perhaps you can put all of your widgets inside the ListView and then you wouldn't need a ScrollView.
You can try set Listview's height at runtime by using this snippet
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup)
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
And example to use this
listComment.setAdapter(new CommentAdapter(comment,
AppDetailActivity.this));
listComment.postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Utils.setListViewHeightBasedOnChildren(listComment);
}
}, 500);
Currently i am displaying listview using visibility "GONE" and then "VISIBLE" on button click, But its displaying only 1 item in listview, other elements in scrollview ( i need to scroll), so i decided to keep listview showing atleast 3 items first and rest element on listview scroll.
how to work out this, thanks in advance.
1st image is my code
i need to have like 2nd image
A ListView inside a ScrollView is a bad idea in general.
Instead you should use a LinearLayout and inflate your ListItems in there.
Not too hard to do. Just use a for loop or something.
If you MUST use a ListView you can call the following method on your ListView after it has been populated.
(change listAdapter.getCount() to the number of cells you want to show
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
I have added Button and ListView on a ScrollView. Button is added below to the ListView in ScrollView but Problem is that it shows large space between button and ListView. Here is my code
ReservationDrinkListAdapter adp = new ReservationDrinkListAdapter(this,
KukumberApplication.getInstance().getBottleService().menuItems);
list.setAdapter(adp);
Util.setListViewHeightBasedOnChildren(list);
following method is used to show ListView in ScrollView.
public static void setListViewHeightBasedOnChildren(ListView listView)
{
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
What am I doing wrong? Any Suggestion and sample code Would be appreciated.
Using a listview inside of a scrollview is technically possible but is not at all recommended, doing as you have (expanding the whole list so the scrollview accomodates all the list) goes against the whole point of adapterviews (performance) and will cause you lots of issues such as those you have encountered.
You can see more about this in answers such as:
How can I put a ListView into a ScrollView without it collapsing?
How to add two listview in scrollview
Why ListView cannot be used in a ScrollView?
ListView inside ScrollView is not scrolling on Android
And there are even more out there.
I have a number of vertically stacked panels in my UI. Each panel contains a ListView. Based on user interaction, the number of items in the ListView gets updated.
My problem is that if I increase the number of items being shown in the ListView, the containing panel will not expand to show them. Instead, my ListView just gets cut off with a fade to black. I am programmatically creating each of these stacked panels - this is the body of the creating function:
LinearLayout containingPanel = new LinearLayout(TestActivity.this);
containingPanel.setOrientation(LinearLayout.VERTICAL);
// create title
TextView titleText = new TextView(TestActivity.this);
titleText.setText("a title");
titleText.setGravity(Gravity.CENTER);
titleText.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
// create dynamic list view of costs
ListView dynamicContentListView = new ListView(TestActivity.this);
dynamicContentListView.setAdapter(new MyDynamicAdapter());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.weight = 1;
dynamicContentListView.setLayoutParams(params);
// build up GUI
containingPanel.addView(titleText);
containingPanel.addView(dynamicContentListView);
return containingPanel;
I thought that setting the weight on the ListView should be enough, but it is not. All GUI updating is working fine - new items are automatically added to data backing the Adapter, and the ListView updates itself properly. But after I add three or four new items, the containingPanel refuses to update itself and the new items get blended out.
try this function. I think, it might help you. The function is used to set ListView's height based on its children.
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(), MeasureSpec.AT_MOST);
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
listView.requestLayout();
}
(You can add a ListView inside a ScrollView but not without a little work (as they are both Scrollable components - how would the OS know which one you're trying to scroll?). You would need to add isScrollContainer="false" on your ListView.)
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_contents"
android:isScrollContainer="false"/>
The entire point of a ListView is it has a set height as dictated by the layout of your page. It only becomes scrollable when it's children's combined height exceed the area required to display it.
It sounds like what you actually want is something more akin to a LinearLayout which is backed by an Adapter, there are several implementations out there on the web or you can create your own.
However, you can hack a ListView into this behaviour by dynamically resizing your ListView programatically by setting it's Height to: listCount * itemHeight. This would have the effect of consistently expanding your ListView.
You will likely find that as you develop your UI design you will no longer require such a component.
If your listview is a default size, it might not be able to fit them in the layout. You could try to wrap the listview in a scrollview and then you can scroll through them.