Creating a ListView with custom list items programmatically in Android - no xml list item layout

daryl picture daryl · Oct 8, 2012 · Viewed 24.1k times · Source

As I have seen on previously asked questions, inside the custom adapter class (say, MyAdapter extends ArrayAdapter) they always use an inflated xml list-item layout. What I am hoping to do is create everything entirely using Java and no XML...

// for example
String[] wordlist = new String[] {a, b, c};

LinearLayout list_item_layout = new LinearLayout(this);
list_item_layout.setId(5000);

TextView listText = new TextView(this);
listText.setId(5001);

listLayout.addView(listText);

ListView list = new ListView(this);

// ** QUESTION ** do I declare a programmatic .setAdapter() like this?
// ** TAKE NOTE ** I passed 'wordlist' here..
list.setAdapter(new MyAdapter(this, list_item_layout.getId(), listText.getId(), wordlist));

and then for MyAdapter...

private class MyAdapter extends ArrayAdapter<String> {

    public MyAdapter(Context context, int resource, int textViewResourceId, String[] strings) {
        super(context, resource, textViewResourceId, strings);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View v = convertView; //is this the list_item_layout that I passed??
        TextView tv = (TextView) v.findViewById(5001);

        // ** QUESTION ** do I pass 'wordlist' again here?
        tv.setText( wordlist[position] );

        return v;
    }
}

What happens when I run this on my device is I get the following errors...

    10-08 23:11:19.775: E/AndroidRuntime(18276): FATAL EXCEPTION: main
    10-08 23:11:19.775: E/AndroidRuntime(18276): android.content.res.Resources$NotFoundException: String resource ID #0x0
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.content.res.Resources.getText(Resources.java:222)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.widget.TextView.setText(TextView.java:3011)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at com.turista.client.TuristaClientMain.onClick(TuristaClientMain.java:113)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.view.View.performClick(View.java:2538)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.view.View$PerformClick.run(View.java:9152)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.os.Handler.handleCallback(Handler.java:587)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.os.Handler.dispatchMessage(Handler.java:92)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.os.Looper.loop(Looper.java:123)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at android.app.ActivityThread.main(ActivityThread.java:3691)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at java.lang.reflect.Method.invokeNative(Native Method)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at java.lang.reflect.Method.invoke(Method.java:507)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
    10-08 23:11:19.775: E/AndroidRuntime(18276):    at dalvik.system.NativeStart.main(Native Method)

Can anyone explain how to do this programmatically?


* EDITED * October 9, 2012


Ok, as I am still stuck in this issue I think I've made some improvements but still get error messages. The improved code is as follows..

//wordlist is a global variable
String[] wordlist = new String[] {a, b, c};

// ..
// .. inside onCreate...

ListView list = new ListView(this);
        list.setAdapter(new MyAdapter(this, R.layout.listitem, R.id.mLargeTextView, wordlist));
// since the ArrayAdapter class needs XML parameters to inflate, I created a dummy layout

now inside MyAdapter class..

private class MyAdapter extends ArrayAdapter<String> {

    public MyAdapter(Context context, int resource, int textViewResourceId, String[] strings) {
        super(context, resource, textViewResourceId, strings);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        LinearLayout listLayout = new LinearLayout(Main.this);
        listLayout.setLayoutParams(new LayoutParams(wrapContent, wrapContent));
        listLayout.setId(5000);

        TextView listText = new TextView(Main.this);
        listText.setId(5001);

        listLayout.addView(listText);

        listText.setText(wordlist[position]);

        return listLayout; 
    }
}

As you can see, I think I have to override getView in order to display my custom view so this is what I did. Unfortunately I think I misunderstood it. Here are the errors..

    10-09 09:24:10.095: E/AndroidRuntime(2517): FATAL EXCEPTION: main
    10-09 09:24:10.095: E/AndroidRuntime(2517): java.lang.ClassCastException: android.view.ViewGroup$LayoutParams
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.ListView.measureScrapChild(ListView.java:1183)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.ListView.measureHeightOfChildren(ListView.java:1266)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.ListView.onMeasure(ListView.java:1175)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1017)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.LinearLayout.measureVertical(LinearLayout.java:386)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.LinearLayout.onMeasure(LinearLayout.java:309)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at com.android.internal.widget.WeightedLinearLayout.onMeasure(WeightedLinearLayout.java:60)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.View.measure(View.java:8366)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewRoot.performTraversals(ViewRoot.java:847)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1868)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.os.Handler.dispatchMessage(Handler.java:99)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.os.Looper.loop(Looper.java:123)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at android.app.ActivityThread.main(ActivityThread.java:3691)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at java.lang.reflect.Method.invokeNative(Native Method)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at java.lang.reflect.Method.invoke(Method.java:507)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
    10-09 09:24:10.095: E/AndroidRuntime(2517):     at dalvik.system.NativeStart.main(Native Method)

Answer

Praful Bhatnagar picture Praful Bhatnagar · Oct 9, 2012

ListView extends AbsListView which in turn extends AdapterView which extends view group. So all the child of ListView would be added to ViewGroup. So to set the layout param, you can use ViewGroup.LayoutParam while setting layout param for listLayout.

Or try AbsListView.LayoutParams for setting layout param for listLayout

Please try it and let me know if it worked.

Following code is working fine..

public class MainActivity1 extends Activity {

    String[] wordlist = new String[] { "a", "b", "c" };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListView list = new ListView(this);
        list.setAdapter(new MyAdapter(this, wordlist));

        setContentView(list);
    }

    private class MyAdapter extends ArrayAdapter<String> {

        public MyAdapter(Context context, String[] strings) {
            super(context, -1, -1, strings);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            LinearLayout listLayout = new LinearLayout(MainActivity1.this);
            listLayout.setLayoutParams(new AbsListView.LayoutParams(
                    AbsListView.LayoutParams.WRAP_CONTENT,
                    AbsListView.LayoutParams.WRAP_CONTENT));
            listLayout.setId(5000);

            TextView listText = new TextView(MainActivity1.this);
            listText.setId(5001);

            listLayout.addView(listText);

        listText.setText(super.getItem(position));

            return listLayout;
        }
    }
}

The problem was that by default Eclipse imports ViewGroup.LayoutParams which are incompatible with the AbsListView.LayoutParams which need to be used for setting layout param for view returned from the getView() method.

Please check and let me know how it went..