0

I'm one of the poor noobs who wanted to put a ListView inside a ScrollView.

Now I understand that they don't want nested scrollable elements - I'm not sure that I agree with their design but I can understand it. What I cannot understand is why I can't make a non-scrollable ListView and nest it into another Scrollable element.

listView.setScrollContainer(false) seemed like the solution (because I thought it could turn off the scrollability of the ListView) but no luck.

So can someone explain why Google won't let listViews not scroll - is there a way to do this that this numpty is just oblivious to?

jcuenod
  • 55,835
  • 14
  • 65
  • 102

2 Answers2

3

You could use a LinearLayout as your non-scrollable ListView. A lot of the power of ListView is how it deals with lazy-loading of elements. This doesn't really make sense in the context of a non-scrollable version.

But really, you most likely need to rethink your UI. Why do you need multiple scrolling sections? That seems really unfriendly from a user's perspective. You can easily place things above and below a ListView via a header or footer.

Cheryl Simon
  • 46,552
  • 15
  • 93
  • 82
  • I want the ListView for the sake of adding and removing elements by index and basically just attached to an arraylist. If you can do that with a LinearLayout please tell me how. – jcuenod Jan 11 '11 at 15:17
  • You can probably do that with a TableLayout: http://developer.android.com/reference/android/widget/TableLayout.html – Cheryl Simon Jan 11 '11 at 16:56
  • `addView` looks promising on the TableLayout but is there some sort of equivalent to `setAdapter`? – jcuenod Jan 12 '11 at 11:29
  • @j3frea No, you need to handle adding views yourself. It is basically just a LinearLayout that lets you add and remove particular rows. – Cheryl Simon Jan 12 '11 at 18:07
2

Like Mayra said, you should rethink your UI.

Unless you have multiple ListViews, I can see a solution to your problem. Since you seem to have only one list view in your scroll view. That means that you wanted to put something above or below.

You there have setHeader, setFooter. There you just have to put anything you want. You have to add theses views before setAdapter().

If you have multiple ListViews in your scrollview. I have a second solution for you. I can't really elaborate. But you'd have to create a custom adapter and override ViewTypeCount so you can handle multiple types of items. That way you can load everything you want through the adapter. And all you really need is a ListView.

If you want to handle multiple types of items (multiple listviews) here is an example "untested"

You have to subclass adapter and override getView. Habitually we should have something similar to that for view recycling

if(view == null){
    //inflate view
}

In your case, you'll need to do that.

package com.neutrino.lvt;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class Main extends Activity {
    private ListView lv;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        lv = (ListView)findViewById(R.id.lv);

        ArrayList<Items> items = new ArrayList<Items>();

        items.add(new ItemA("Une"));
        items.add(new ItemA("Une3"));
        items.add(new ItemA("Une4"));
        items.add(new ItemA("Une5"));
        items.add(new ItemA("Une6"));
        items.add(new ItemA("Une7"));
        items.add(new ItemA("Une8"));
        items.add(new ItemA("Une9"));
        items.add(new ItemA("Une0"));
        items.add(new ItemA("Une0-"));
        items.add(new ItemB("bbb", "cc1c"));
        items.add(new ItemB("bbb3", "c2cc"));
        items.add(new ItemB("bbb4", "c2cc"));
        items.add(new ItemB("bbb5", "c1cc"));
        items.add(new ItemB("bbb6", "cc5c"));
        items.add(new ItemB("bbb7", "cc4c"));
        items.add(new ItemB("bbb7", "c2cc"));
        items.add(new ItemB("bbb8", "c1cc"));
        items.add(new ItemB("bbb9", "cc6c"));
        items.add(new ItemB("bbb0", "cc8c"));
        items.add(new ItemB("bbb0", "cc0c"));
        items.add(new ItemB("bb3b", "cc6c"));
        items.add(new ItemA("Une"));
        items.add(new ItemA("Une3"));
        items.add(new ItemA("Une4"));
        items.add(new ItemA("Une5"));
        items.add(new ItemA("Une6"));
        items.add(new ItemA("Une7"));
        items.add(new ItemA("Une8"));
        items.add(new ItemA("Une9"));
        items.add(new ItemA("Une0"));
        items.add(new ItemA("Une0-"));
        items.add(new ItemB("bbb6", "cc5c"));
        items.add(new ItemB("bbb7", "cc4c"));
        items.add(new ItemB("bbb7", "c2cc"));
        items.add(new ItemB("bbb8", "c1cc"));
        items.add(new ItemB("bbb9", "cc6c"));
        items.add(new ItemB("bbb0", "cc8c"));
        items.add(new ItemB("bbb0", "cc0c"));
        items.add(new ItemB("bb3b", "cc6c"));

        SpecialAdapter b = new SpecialAdapter(this, items);
        lv.setAdapter(b);

    }

    private class SpecialAdapter extends BaseAdapter{

        List<Items> list;
        Context context;

        public SpecialAdapter(Context context, List<Items> list) {
            this.list = list;
            this.context = context;
        }

        public Context getContext(){
            return this.context;
        }

        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Items getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return list.get(position).hashCode();
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getItemViewType(int position) {
            Items b = getItem(position);
            if(b instanceof ItemA){
                return 0;
            }else{
                return 1;
            }
        }



        @Override
        public View getView(int position, View v, ViewGroup parent) {
            Items o = getItem(position);
            if(v == null){
                LayoutInflater vi = (LayoutInflater)this.getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);     
                if(getItemViewType(position) == 0){
                    v = vi.inflate(
                                     android.R.layout.simple_list_item_multiple_choice,
                                     null
                                    );
                    Log.i(this.getClass().getName(), 
                                        "Inflate two line"
                                    );
                }else{
                    v = vi.inflate(
                                     android.R.layout.two_line_list_item, 
                                     null
                                    );
                    Log.i(this.getClass().getName(), 
                                     "Inflate one line"
                                    );
                }
            }

            switch (getItemViewType(position)) {
            case 0:
                ItemA oa = (ItemA)o;
                ((TextView)v.findViewById(android.R.id.text1)).setText(oa.toString());
                break;
            case 1:
                ItemB ob = (ItemB)o;
                ((TextView)v.findViewById(android.R.id.text1)).setText(ob.toString());
                ((TextView)v.findViewById(android.R.id.text2)).setText(ob.getClasse());
                break;
            }
            return v;
        }

    }

    private abstract class Items {}

    private class ItemA extends Items {
        String name;

        public ItemA(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "ItemA: " + name;
        }
    }

    private class ItemB extends Items {
        private String name;
        private String classe;

        public ItemB(String name, String classe) {
            this.name = name;
            this.classe = classe;
        }

        public String getClasse(){
            return classe;
        }

        @Override
        public String toString() {
            return "ItemB: " + name;
        }
    }
}

I'll try to write a sample and come up with an update soon. I think I'm missing something because when views are recycled you need to check if the recycled view correspond to the ItemType. But you can definitely do it through a ListView alone. No scrollable needed.

Loïc Faure-Lacroix
  • 13,220
  • 6
  • 67
  • 99
  • Actually I want to use the adaptability of the ListView - the way that I can quickly insert and remove elements (by Index) with the adaptor. If there's a way to do that with LinearLayout let me know. But I need more than one ListView which is why I wanted to put both into the ScrollView (I don't actually even want the ListViews scrollable). – jcuenod Jan 11 '11 at 15:15
  • Tell me if the code seems clear? You need to handle multiple view types 1 for each of the list views you had. And you can even add a sort to your items so ItemA is always on top of ItemB. – Loïc Faure-Lacroix Jan 11 '11 at 21:10
  • It makes sense - but this is my point. Why does Google not just allow nested scrollable elements. Let us make the UI decisions and if we're failures then so be it but give us the functionality... I understand your code but my point is that it's unnecessary. – jcuenod Jan 12 '11 at 11:25
  • @j3frea Why should Google waste there time implementing something that they know would be a really bad user experience? There are plenty of other things they can be focusing on that will be useful to far more people. – Cheryl Simon Jan 12 '11 at 18:06
  • 2
    @j3frea there is a good reason why its not possible. The Scroller doesn't recycle views. The ListView recycle views and is memory efficient. If the list view was expanded in a scrollable, there wouldn't view recycling if you have 100 items it equals to a minimum of 100 views in memory. In my code above there would be at most 20 views for 20, 100, 1000 items. So yes the code I posted is necessary. The scroller view cannot recycle anything by itself. – Loïc Faure-Lacroix Jan 13 '11 at 01:59
  • @Mayra just because the UI may not work on a 3.5" screen doesn't mean it won't work on a 10" one. – jcuenod Jan 13 '11 at 16:33
  • Well actually you're still quite limited by the screen resolution.But sure it will be more finger friendly on a 10" screen. – Loïc Faure-Lacroix Jan 14 '11 at 11:07