39

I'm having a little difficulties while trying to get a certain layout to work: I want to have list. List does not have to be scrollable, but should be shown completely. But the page itself should be able to scroll (with the lists in it), if the total content ist higher than the screen.

<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     >

     <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/linear_layout"
         android:orientation="vertical"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:background="#ff181818"
         >
           <Textview android:id="@+id/my_text" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
           <Textview android:id="@+id/headertext" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

          <ListView
               android:id="@+id/my_list1"
               android:layout_height="wrap_content"
               android:layout_width="fill_parent"
          /> 
     </LinearLayout> 

</ScrollView>

it only uses a small part of the screen (about 2 lines per list), instead of filling the available height, and the lists themselves can be scrolled. How can I change the layout to always show the whole lists but have the screen be scrollalbe?

poke
  • 369,085
  • 72
  • 557
  • 602
d-man
  • 57,473
  • 85
  • 212
  • 296
  • http://www.anddev.org/viewtopic.php?p=25194 On another forum i found comment some thing like this "since a ListView already has scrolling capacity. Therefore I have rewritten my custom list to inherit from LinearLayout, which works fine in a ScrollView. I don't know if that's the only way, but it achieves what I wanted to do for now." can you guys help me out how to code above scenario ? – d-man Nov 22 '09 at 19:26
  • ..could you please provide us with some code and the xml layout in which you could achieve the entire the screen to be scrolled...thanks... – i_raqz Jun 22 '11 at 17:26

13 Answers13

65

The solution I used is to replace ListView with LinearLayout. You can create all your items inside LinearLayout, they will all be displayed. So there's really no need to use ListView.

LinearLayout list = (LinearLayout)findViewById(R.id.list_recycled_parts);
for (int i=0; i<products.size(); i++) {
  Product product = products.get(i);
  View vi = inflater.inflate(R.layout.product_item, null);
  list.addView(vi);
}
Fedor
  • 43,261
  • 10
  • 79
  • 89
  • Shouldn't you pass the object to inflate? – dierre Mar 11 '12 at 21:50
  • what is R.layout.product_item?? – Ken Aug 30 '12 at 12:24
  • 51
    Linear layout is hardly a replacement. List view has built-in adapter (to fill the item views), dividers between items, item states, item click event. – Alex Mar 16 '13 at 22:34
  • 1
    But how can we handle OnItemClick? –  Feb 03 '14 at 03:38
  • 6
    Not to mention, listview has performance benefits in reusing the row views. If you have a large number of rows then linear layout isn't going to work, scrolling will be extremely choppy. – Justin Aug 15 '14 at 17:49
  • What about `ListView`'s benefits? Are they all gone because of the scroll? – Aritz Oct 09 '14 at 11:27
  • This is a roundabout at best, not real solution. – Behnam Oct 22 '14 at 11:19
  • 1
    This is bad approach, because ListView can use Adapter, and LinearLayout can't. – Fire Mar 11 '15 at 11:47
  • I know everyone says this is a bad approach but a ListView shouldnt be placed inside of a ScrollView. This approach would allow such if you don't need click ability and are only showing a few things. FYI. For those wondering you can populate using the "vi" view as it is your layout for the row. Just my 2 cents. – Micah Montoya Jun 23 '16 at 18:15
  • You can follow this answer: https://stackoverflow.com/questions/40861136/set-listview-height-programmatically – Akhtar Khan Apr 01 '19 at 13:02
48

As @Alex noted in the accepted answer that LinearLayout is hardly a replacement. I had a problem where LinearLayout was not an option, that's when i came across this blog. I will put the code here for reference purposes. Hope it helps someone out there!

public class UIUtils {

    /**
     * Sets ListView height dynamically based on the height of the items.
     *
     * @param listView to be resized
     * @return true if the listView is successfully resized, false otherwise
     */
    public static boolean setListViewHeightBasedOnItems(ListView listView) {

        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter != null) {

            int numberOfItems = listAdapter.getCount();

            // Get total height of all items.
            int totalItemsHeight = 0;
            for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
                View item = listAdapter.getView(itemPos, null, listView);
                item.measure(0, 0);
                totalItemsHeight += item.getMeasuredHeight();
            }

            // Get total height of all item dividers.
            int totalDividersHeight = listView.getDividerHeight() *
                    (numberOfItems - 1);

            // Set list height.
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalItemsHeight + totalDividersHeight;
            listView.setLayoutParams(params);
            listView.requestLayout();

            return true;

        } else {
            return false;
        }

    }
}

Usage:

//initializing the adapter
listView.setAdapter(adapter);
UIUtils.setListViewHeightBasedOnItems(listView);

//whenever the data changes
adapter.notifyDataSetChanged();
UIUtils.setListViewHeightBasedOnItems(listView);
Noah Andrews
  • 353
  • 2
  • 14
HussoM
  • 1,272
  • 13
  • 7
17

You can make your own customlistview. (It can extends ListView/ExpandableListView/GridView) and override the onMeasure method with this. With this you'll never need to call a function or anything. Just use it in your xml.

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}
Francisco
  • 254
  • 3
  • 9
  • This is a much better answer as it is an extensible and clean approach. – Gautham C. Feb 07 '17 at 17:19
  • It looks very clean. I wonder with this one can we place listview inside scrollview? – anhtuannd Apr 19 '17 at 05:23
  • 1
    @anhtuannd I haven't try that... But the only thing that change is the onMeasure method, all the scroll methods of the ListView are still working but with nothing to scroll. So if the listview is inside the scrollview I think it will still override those methods. I would look for another approach to do that. – Francisco Apr 20 '17 at 14:27
13

I had a ListView in my layout and wanted to use a library which can't handle a ListView here because it wraps it into a ScrollView. The best solution for me is based on Fedor´s answer.

Since I already got an ArrayAdapter for the ListView I wanted to re-use it:

LinearLayout listViewReplacement = (LinearLayout) findViewById(R.id.listViewReplacement);
NamesRowItemAdapter adapter = new NamesRowItemAdapter(this, namesInList);
for (int i = 0; i < adapter.getCount(); i++) {
    View view = adapter.getView(i, null, listViewReplacement);
    listViewReplacement.addView(view);
}

For me this works fine because I just need to display dynamic data varying from 1 to 5 elements. I just had to add my own divider.

Kai
  • 38,985
  • 14
  • 88
  • 103
5

If someone still has the problem then you can make customList and add onMesure() method just like I implemented it:

public class ScrolleDisabledListView extends ListView {

private int mPosition;

public ScrolleDisabledListView(Context context) {
    super(context);
}

public ScrolleDisabledListView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ScrolleDisabledListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
            super.dispatchTouchEvent(ev);
        } else {
            // Clear pressed state, cancel the action
            setPressed(false);
            invalidate();
            return true;
        }
    }

    return super.dispatchTouchEvent(ev);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}
}
davejal
  • 6,009
  • 10
  • 39
  • 82
Rojina
  • 217
  • 1
  • 3
  • 9
  • This is exactly what I was looking for! I have a ListView, it contains 3-4 items. I cannot use static elements as I need all the functionality of the ListView and that solved my problem when I put it in the ScrollView! – Геннадий Курбесов Dec 17 '20 at 15:13
1

I just did it using setting params of ListView

public static void setListViewHeightBasedOnChildren(ListView listView) {

    //this comes from value from xml tag of each item
    final int HEIGHT_LARGE=75;
    final int HEIGHT_LARGE=50;
    final int HEIGHT_LARGE=35;
    ViewGroup.LayoutParams params = listView.getLayoutParams();

    int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;

    switch(screenSize) {

    case Configuration.SCREENLAYOUT_SIZE_LARGE:
         params.height =(int) (HEIGHT_LARGE*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_NORMAL:
         params.height =(int) (HEIGHT_NORMAL*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_SMALL:
          params.height =(int) (HEIGHT_SMALL*size);
          break;
    }
    listView.setLayoutParams(params);  
}
cakan
  • 2,099
  • 5
  • 32
  • 42
Ivan
  • 11
  • 2
1

I don't have a static header, but using HussoM's post as a clue, here is what I was able to get to work. In my scenario, the height of the items in the list was non-uniform, due to variable text sentences in each of the items, and I am using wrap_content for the height and match_parent for the width.

public class NonScrollableListView extends ListView {

  public NonScrollableListView(Context context) {
      super(context);
  }

  public NonScrollableListView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
  }

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  /**
   * Measure the height of all the items in the list and set that to be the height of this
   * view, so it appears as full size and doesn't need to scroll.
   * @param widthMeasureSpec
   * @param heightMeasureSpec
   */
  @Override
  public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      ListAdapter adapter = this.getAdapter();
      if (adapter == null) {
          // we don't have an adapter yet, so probably initializing.
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          return;
      }

      int totalHeight = 0;

      // compute the height of all the items
      int itemCount = adapter.getCount();
      for (int index=0; index<itemCount; index++) {
          View item = adapter.getView(index, null, this);
          // set the width so it can figure out the height
          item.measure(widthMeasureSpec, 0);
          totalHeight += item.getMeasuredHeight();
      }

      // add any dividers to the height
      if (this.getDividerHeight() > 0) {
          totalHeight += this.getDividerHeight() * Math.max(0, itemCount - 1);
      }

      // make it so
      this.setMeasuredDimension(widthMeasureSpec,
              MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY));
  }

}

cmarcelk
  • 306
  • 1
  • 4
1

Check this out:

ListView ignoring wrap_content

Using android:layout_height and android:layout_weight solved it for me:

<ListView
    android:layout_height="0dp"
    android:layout_weight="1"
    />
Community
  • 1
  • 1
tinsukE
  • 930
  • 9
  • 20
0

If all items has the same height

        int totalItemsHeight = baseDictionaries.size() * item.getMeasuredHeight();
        int totalDividersHeight = listView.getDividerHeight() * (baseDictionaries.size() - 1);
        int totalPadding = listView.getPaddingBottom() + listView.getPaddingTop();

        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) listTranslationWords.getLayoutParams();
        lp.height = totalItemsHeight + totalDividersHeight + totalPadding;
        listTranslationWords.setLayoutParams(lp);
Vahe Gharibyan
  • 5,277
  • 4
  • 36
  • 47
0

Iam supprised no one see this.U cant have two scrolls on the same layout. 1st u have a scrollview and then u have a list, i bet u are killing some android good practices there.

Catluc
  • 1,775
  • 17
  • 25
0

If you want a simple solution to this problem without extending ListView class, this is a solution for you.

 mListView.post(new Runnable() {
        @Override
        public void run() {
            int height = 0;
            for(int i = 0; i < mListView.getChildCount();i++)
                height += mListView.getChildAt(i).getHeight();
            ViewGroup.LayoutParams lParams = mListView.getLayoutParams();
            lParams.height = height;
            mListView.setLayoutParams(lParams);
        }
    });
Yan
  • 1,569
  • 18
  • 22
0

In my case, I had ListView inside ScrollView and scrollview was shrinking listview by default. So I just add this in my ScrollView and it worked for me

android:fillViewport="true"
Zohab Ali
  • 8,426
  • 4
  • 55
  • 63
-6

Set android:layout_height="fill_parent" in your LinearLayout

Lucifer
  • 29,392
  • 25
  • 90
  • 143
  • 1
    Nope, sadly this will not help - a ListView will always attempt to scroll its contents. So although it's not as nice as ListView, LinearLayout is the way to go here. – Konrad 'ktoso' Malawski Sep 21 '11 at 23:38