I know it is a very bad practice that "don't put a ListView into a ScrollView" since ListView has it's own scroll. However, I have couple of other items (buttons and textviews) additional to ListView. For those, I definitely need to use a ScrollView. I found some solution (http://nex-otaku-en.blogspot.com/2010/12/android-put-listview-in-scrollview.html) to prevent collapsing the Listview. But, as i said, I need a ScrollView that encapsulates all items on my XML form. I have added my xml code, Please guide how to achieve this.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/listView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:choiceMode="singleChoice">
</ListView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button1" />
</LinearLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/longtext" />
</LinearLayout>
</ScrollView>
ListView by itself is scrollable. Do not put ListView inside a scroll view.
Move your ListView outside scorllview. You can also add a header and footer to the listview.
Check the video by google
https://www.youtube.com/watch?v=wDBM6wVEO70
I found this solution from another post & found it working.
While all other solutions work when the list-items are of same height, this solution still provides perfect result even if the list-items are of variable height.
Use this customized class in the xml & you are good to go.
public class NestedListView extends ListView implements OnTouchListener, OnScrollListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null, this);
//now it will not throw a NPE if listItem is a ViewGroup instance
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
You can use the Expandable Listview for using the listview inside the scrollview, it will expends the listview as long the content length, create a Custom class with name ExpandableHeightListView
package com.knight.utils;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ListView;
public class ExpandableHeightListView extends ListView
{
boolean expanded = false;
public ExpandableHeightListView(Context context)
{
super(context);
}
public ExpandableHeightListView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ExpandableHeightListView(Context context, AttributeSet attrs,int defStyle)
{
super(context, attrs, defStyle);
}
public boolean isExpanded()
{
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// HACK! TAKE THAT ANDROID!
if (isExpanded())
{
// Calculate entire height by providing a very large height hint.
// But do not use the highest 2 bits of this integer; those are
// reserved for the MeasureSpec mode.
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
else
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded)
{
this.expanded = expanded;
}
}
After that Programmatically add this line into your code, it will expend the listview in side the scrollview,
Listview listview=(Listview)findviewbyid(R.id.listview1);
((ExpandableHeightListView)listview).setExpandable(true);
Related
I have a layout made of a MapView and a Listview 50-50 (image link below)
http://imgur.com/uRIS9AN
I want to handle the scroll event of the ListView, to expand it to fullscreen before it scrolls through the elements of the list.
Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.gms.maps.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fortress_listview">
</ListView>
</LinearLayout>
</LinearLayout>
take a look on my code which i am using here, Use this as in xml class :
<NestedListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fortress_listview">
Add this NestedListView Class in your app package :
public class NestedListView extends ListView implements View.OnTouchListener, AbsListView.OnScrollListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null, this);
//now it will not throw a NPE if listItem is a ViewGroup instance
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
I'd like to use listview with custom adapter and it dynamically changing.
There is that i want (element above ListView must be scrolled):
I found two main ways to do it:
use ScrollView and code with listView.measure(0,0); to dynamically set up the listview height (but it doesn't work, listview is cropped);
For example: listView have 3 items, but it height is for 2 items (1 item is hidden);
don't use ScrollView, use a setHeaderView (but it doesn't work too, ListView don't have a scrolling)
Any idea?
Use custom ListView ExpandableHeightListView here
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.ListView;
public class ExpandableHeightListView extends ListView
{
boolean expanded = false;
public ExpandableHeightListView(Context context)
{
super(context);
}
public ExpandableHeightListView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ExpandableHeightListView(Context context, AttributeSet attrs,int defStyle)
{
super(context, attrs, defStyle);
}
public boolean isExpanded()
{
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// HACK! TAKE THAT ANDROID!
if (isExpanded())
{
// Calculate entire height by providing a very large height hint.
// But do not use the highest 2 bits of this integer; those are
// reserved for the MeasureSpec mode.
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
else
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded)
{
this.expanded = expanded;
}
}
use
list.setExpanded(true);
in onCreate() method.
i have a fragment in one of my apps who looks pretty much the same..
and I had the same problem as you, what I did in my case was set up the size for each of my list view cells at a fixed size. and measure the height by that logic.
not sure if that's considered the best way to achieve that, but it worked for me.
filterListView.getLayoutParams().height = (searchLabelsList.size() * (int) (43 * getScale() + 0.5f)) + (filterListView.getDividerHeight() * (searchLabelsList.size() - 1));
filterListView.setAdapter(searchLabelsAdapter);
and in the formula, 43 is the height for each cell, in dp's of course.
getScale is a method I wrote to get the scale of the screen size of the current user phone:
private float scale;
private float getScale() {
if (scale == 0) {
if (getActivity() != null) {
scale = getActivity().getResources().getDisplayMetrics().density;
}
}
return scale;
}
hope this will help, any question feel free to ask :)
good luck
You can try this:
First : In your xml put all other views inside ScrollView including ListView
<ScrollView
android:id="#+id/scrollViewId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
// Add your other views over here....
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
Second : In your java file,
Just use this custom method setListViewHeightBasedOnChildren(listview)
How ??
list = (ListView) findViewById(R.id.listview);
list.setAdapter(YOUR CUSTOM ADAPTER);
setListViewHeightBasedOnChildren(list);
Here is your custom method.
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.MATCH_PARENT));
view.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
totalHeight += view.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + ((listView.getDividerHeight()) * (listAdapter.getCount()));
listView.setLayoutParams(params);
listView.requestLayout();
}
Hope this helps you somehow.
I have following code:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 2"
android:textSize="24sp" />
</LinearLayout>
that results:
How can I have equal size of View's height with its width (that is resulted by layout_weight)?
You have to override the onMeasure method set the same height as the height. Please see this question: Simple way to do dynamic but square layout
This is the complete solution that I come up with:
SquareLinearLayout.java :
public class SquareLinearLayout extends LinearLayout {
public SquareLinearLayout(Context context) {
super(context);
}
public SquareLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthDesc = MeasureSpec.getMode(widthMeasureSpec);
int heightDesc = MeasureSpec.getMode(heightMeasureSpec);
int size = 0;
if (widthDesc == MeasureSpec.UNSPECIFIED
&& heightDesc == MeasureSpec.UNSPECIFIED) {
size = getContext().getResources().getDimensionPixelSize(R.dimen.default_size); // Use your own default size, for example 125dp
} else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED)
&& !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) {
//Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0)
size = width > height ? width : height;
} else {
//In all other cases both dimensions have been specified so we choose the smaller of the two
size = width > height ? height : width;
}
setMeasuredDimension(size, size);
}
}
activity_my.xml :
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:listitem="#layout/item"
tools:context=".MyActivity">
</ListView>
item.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.appsrise.squarelinearlayout.SquareLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/holo_blue_bright">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</com.appsrise.squarelinearlayout.SquareLinearLayout>
<com.appsrise.squarelinearlayout.SquareLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/holo_green_light">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 2"
android:textSize="24sp" />
</com.appsrise.squarelinearlayout.SquareLinearLayout>
</LinearLayout>
MyActivity.java :
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(new BaseAdapter() {
private LayoutInflater mInflater = LayoutInflater.from(MyActivity.this);
#Override
public int getCount() {
return 100;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.item, parent, false);
return convertView;
}
});
}
}
Simpler solution is to pass the width measurement specification into the height specification when calling onMeasure (or the other way around if you want the height to determine the square size).
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SquareLinearLayout extends LinearLayout{
public SquareLinearLayout(Context context) {
super(context);
}
public SquareLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
// or super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}
Xamarin version
[Register("SquareFrameLayout")]
public class SquareFrameLayout : LinearLayout
{
public SquareFrameLayout(Context context) : base(context)
{
}
public SquareFrameLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public SquareFrameLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
var width = MeasureSpec.GetSize(widthMeasureSpec);
var height = MeasureSpec.GetSize(heightMeasureSpec);
var widthDesc = MeasureSpec.GetMode(widthMeasureSpec);
var heightDesc = MeasureSpec.GetMode(heightMeasureSpec);
if (widthDesc == MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
//size = Context.Resources.GetDimensionPixelSize(Resource.Dimension.default_size); // Use your own default size, for example 125dp
height = width = 0;
}
else if(heightDesc != MeasureSpecMode.Unspecified && widthDesc == MeasureSpecMode.Unspecified)
{
width = height;
}
else if (widthDesc != MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
height = width;
}
else
{
//In all other cases both dimensions have been specified so we choose the smaller of the two
var size = width > height ? height : width;
width = height = size;
}
SetMeasuredDimension(width, height);
}
and
[Register("SquareLinearLayout")]
public class SquareLinearLayout : LinearLayout
{
public SquareLinearLayout(Context context) : base(context)
{
}
public SquareLinearLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public SquareLinearLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
var width = MeasureSpec.GetSize(widthMeasureSpec);
var height = MeasureSpec.GetSize(heightMeasureSpec);
var widthDesc = MeasureSpec.GetMode(widthMeasureSpec);
var heightDesc = MeasureSpec.GetMode(heightMeasureSpec);
if (widthDesc == MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
//size = Context.Resources.GetDimensionPixelSize(Resource.Dimension.default_size); // Use your own default size, for example 125dp
height = width = 0;
}
else if(heightDesc != MeasureSpecMode.Unspecified && widthDesc == MeasureSpecMode.Unspecified)
{
width = height;
}
else if (widthDesc != MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
height = width;
}
else
{
//In all other cases both dimensions have been specified so we choose the smaller of the two
var size = width > height ? height : width;
width = height = size;
}
SetMeasuredDimension(width, height);
}
}
You can do this also, If you need only one TextView needs to be square :
ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
textview.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = textview.getMeasuredWidth();
int height = textview.getMeasuredHeight();
ViewGroup.LayoutParams params = textview.getLayoutParams();
if (width > height)
params.height = width;
else
params.width = height;
textview.setLayoutParams(params);
}
});
You can divide a LinearLayout into two equal LinearLayout like this..
<LinearLayout android:orientation="horizontal" android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
</LinearLayout>
This will divide the LinearLayout into two equal LinearLayouts and each of these LinearLayouts will contain the TextViews
i'm trying to place a listview inside a listviewitem. the inner listview should not be scrollable but take all size it needs to display all it's rows. is there a better way to to this? table, grid, ...? the problem i'm facing right now is that the inner listview doesn't take the space it needs, so it's cut at about the end of the first listitem. if i try to scroll, just the outer listview is scrolling which is exactly what i want.
thanks, my final solution is
LinearLayout layout = (LinearLayout) row.findViewById(R.id.LLBroadcasts);
layout.removeAllViews();
for (Item b : bs.getItems()) {
View child = _inflater.inflate(R.layout.item_row, null);
TextView tvTitle = (TextView) child.findViewById(R.id.TVItemTitle);
tvTitle.setText(b.getTitle());
TextView tvDesc = (TextView) child.findViewById(R.id.TVItemDescription);
tvDesc.setText(b.getDescription());
layout.addView(child);
}
From the Android documentation - Listview: ListView is a view group that displays a list of scrollable items
You do not really want to scroll that inner list view, you want to scroll the outer listview. However I asume that the inner listview may vary on the amount of elements it contains.
Instead of the inner list view you could use a
linear layout, see this tutorial or look at Adding content to a linear layout dynamically?
table layout
For the linear layout (some sample code):
// access your linear layout
LinearLayout layout = (LinearLayout)findViewById(R.id.layout);
// load the xml structure of your row
View child = getLayoutInflater().inflate(R.layout.row);
// now fill the row as you would do with listview
//e.g. (TextView) child.findViewById(...
...
// and than add it
layout.addView(child);
You should save the linear layout in a view holder (see View Holder pattern). I think the removeAllViews() is only necessary when the current row has lesser inner rows than the reused one, so I would also save the number of rows in the view holder.
If the maximum number of inner rows is not to high you could also think about caching them in the view holder to avoid the inflate and findByViewId (lets say in an ArrayList).
I have the same problem in my App but I needed to use a ListView cause it was a shared item and I didn't want to replicate equal components. So.. I just fixed the size of inner ListView programatically to show all rows and.. voila! Problem solved:
ViewGroup.LayoutParams layoutParams = innerListView.getLayoutParams();
layoutParams.height = (int) context.getResources().getDimension(R.dimen.rowheight) * innerListView.getCount();
innerListView.setLayoutParams(layoutParams);
CustomAdapter adapter = new CustomAdapter(context, blabla..);
innerListView.setAdapter(adapter);
rowListView.invalidate();
Maybe somebody will find my solution useful.
It is based on #ChrLipp answer and uses LinearLayout.
public class NotScrollableListView extends LinearLayout {
private ListAdapter adapter;
private DataChangeObserver dataChangeObserver;
private Drawable divider;
private int dividerHeight;
private List<View> reusableViews = new ArrayList<>();
public NotScrollableListView(Context context) {
super(context);
}
public NotScrollableListView(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
public NotScrollableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setAttributes(attrs);
}
public ListAdapter getAdapter() {
return adapter;
}
public void setAdapter(ListAdapter adapter) {
if (this.adapter != null && dataChangeObserver != null) {
this.adapter.unregisterDataSetObserver(dataChangeObserver);
}
this.adapter = adapter;
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (adapter != null) {
dataChangeObserver = new DataChangeObserver();
adapter.registerDataSetObserver(dataChangeObserver);
fillContents();
}
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (adapter != null) {
adapter.unregisterDataSetObserver(dataChangeObserver);
dataChangeObserver = null;
}
}
private void fillContents() {
// clearing contents
this.removeAllViews();
final int count = adapter.getCount(); // item count
final int reusableCount = reusableViews.size(); // count of cached reusable views
// calculating of divider properties
ViewGroup.LayoutParams dividerLayoutParams = null;
if (divider != null && dividerHeight > 0) {
dividerLayoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dividerHeight);
}
// adding items
for (int i = 0; i < count; i++) {
// adding item
View converView = null;
if (i < reusableCount) { // we have cached view
converView = reusableViews.get(i);
}
View view = adapter.getView(i, converView, this);
if (i >= reusableCount) { // caching view
reusableViews.add(view);
}
addView(view);
// adding divider
if (divider != null && dividerHeight > 0) {
if (i < count - 1) {
ImageView dividerView = new ImageView(getContext());
dividerView.setImageDrawable(divider);
dividerView.setLayoutParams(dividerLayoutParams);
addView(dividerView);
}
}
}
}
private void setAttributes(AttributeSet attributes) {
int[] dividerAttrs = new int[]{android.R.attr.divider, android.R.attr.dividerHeight};
TypedArray a = getContext().obtainStyledAttributes(attributes, dividerAttrs);
try {
divider = a.getDrawable(0);
dividerHeight = a.getDimensionPixelSize(1, 0);
} finally {
a.recycle();
}
setOrientation(VERTICAL);
}
private class DataChangeObserver extends DataSetObserver {
#Override
public void onChanged() {
super.onChanged();
fillContents();
}
#Override
public void onInvalidated() {
super.onInvalidated();
fillContents();
}
}
}
<com.sample.ui.view.NotScrollableListView
android:id="#+id/internalList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#color/list_divider_color"
android:dividerHeight="#dimen/list_divider_width"
/>
I tried making this exact structure (a ListView inside of a ListView) and had the same problem of it only showing the first item of the inner ListView. I fixed it by changing the layout_height of the inner list from match_parent to a set dp.
It seemed to work exactly as I wanted it to.
#Try this nested class
this works for scroll listView inside listView Or 2 listviews in same activity
<com.example.taskgrptaskslistview.NestedListView
android:id="#+id/listviewTasks"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:cacheColorHint="#00000000" >
</com.example.taskgrptaskslistview.NestedListView>
</LinearLayout>
NestedListView :
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListAdapter;
import android.widget.ListView;
public class NestedListView extends ListView implements OnTouchListener, OnScrollListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null, this);
//now it will not throw a NPE if listItem is a ViewGroup instance
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Listview inside ScrollView is not scrolling on Android
I have a ListView inside a ScrollView and the problem is that the ScrollView is scrolling but ListView doesn't scroll. I think this is happening because of that ScrollView. Does anyone have a solution for this issue?
ListViews have built-in scrolling capabilities. Encapsulate it in any other layout like a LinearLayout or RelativeLayout.
I required having a listview inside a scrollview in order to prevent the entire screen from being extremely long. This allows you have to set the number of items displayed on the list, while the rest will be scrolled.
I use the following class in my app and so far it seems to work terrific.
public class NestedListView extends ListView implements OnScrollListener,
OnTouchListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 4;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null,
this);
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
Use the following method and enjoy!
private void setListViewScrollable(final ListView list) {
list.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
listViewTouchAction = event.getAction();
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, 1);
}
return false;
}
});
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE)
{
list.scrollBy(0, -1);
}
}
});
}
listViewTouchAction is a global integer value.
If you can replace the line
list.scrollBy(0, 1);
with something else please share it with us.
You shouldn't nest a ListView inside a ScrollView.
If you're trying to scroll some other views along with the ListView, you might check out this answer.