Favolas
Favolas

Reputation: 7243

ListView containing TabHost with other ListViews

I want to create a ListView with the last row showing a TabHost with two tabs. I would like to load another ListView for each tab. For this, I've created three XML files.

The main.xml is where I'm loading the initial list. The mainlistdata.xml is how each row of the list is presented. And the last xml is tabslayout.xml. This layout is used for the last row on the main listview. It will load the tabhost. Here is the code with all the xml:

My main class is this:

public class MainActivity extends Activity {
    ArrayList<testData> myData = new ArrayList<testData>();
    ListView listContent;
    TestAdapter dataAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        String[] aString = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };

        for (int i = 0; i < aString.length; i++) {
            myData.add(new testData(i, aString[i]));
        }

        listContent = (ListView) findViewById(R.id.mainlist);
        dataAdapter = new TestAdapter(this, myData,1);
        listContent.setAdapter(dataAdapter);
    }
}

And this is the class that loads the content on the listviews and where I set the tabhost:

public class TestAdapter extends ArrayAdapter<testData> {
    private Context context;
    private ArrayList<testData> data;
    private ListView listContent,listContent2;
    public TabHost tabs;
    TestAdapter dataAdapter;
    private int choice;
    public TestAdapter(Context context, ArrayList<testData> data, int choice) {
        super(context, R.layout.mainlistdata, data);
        this.context = context;
        this.data = data;
        this.choice = choice;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView;
        if (choice == 1) {
            if (position == data.size()-1) {
                rowView = inflater.inflate(R.layout.tabslayout, parent, false);

                TextView first = (TextView) rowView.findViewById(R.id.dataTitle);
                TextView second = (TextView) rowView.findViewById(R.id.dataValue);
                TextView third = (TextView) rowView.findViewById(R.id.dataValueString);

                first.setText("Item " + data.get(position).getIntValue());
                second.setText(String.valueOf(data.get(position).getIntValue()));
                third.setText(data.get(position).getStringValue());

                tabs = (TabHost) rowView.findViewById(android.R.id.tabhost);
                tabs.setup();
                TabSpec tab1 = tabs.newTabSpec("Tab 1");
                TabSpec tab2 = tabs.newTabSpec("Tab 2");
                tab1.setContent(R.id.tabOne);
                tab2.setContent(R.id.tabTwo);
                tab1.setIndicator("One");
                tab2.setIndicator("two");
                tabs.addTab(tab1);
                tabs.addTab(tab2);
                tabs.setup();

                listContent = (ListView) rowView.findViewById(R.id.listOne);
                dataAdapter = new TestAdapter(context, data,2);
                listContent.setAdapter(dataAdapter);

                listContent2 = (ListView) rowView.findViewById(R.id.listTwo);
                dataAdapter = new TestAdapter(context, data,3);
                listContent2.setAdapter(dataAdapter);
            } else {
                rowView = inflater.inflate(R.layout.mainlistdata, parent, false);
                TextView first = (TextView) rowView.findViewById(R.id.dataTitle);
                TextView second = (TextView) rowView.findViewById(R.id.dataValue);
                TextView third = (TextView) rowView.findViewById(R.id.dataValueString);

                first.setText("Item " + data.get(position).getIntValue());
                second.setText(String.valueOf(data.get(position).getIntValue()));
                third.setText(data.get(position).getStringValue());
            }
        } else if (choice == 2) {
            rowView = inflater.inflate(R.layout.mainlistdata, parent, false);
            TextView first = (TextView) rowView.findViewById(R.id.dataTitle);
            TextView second = (TextView) rowView.findViewById(R.id.dataValue);
            TextView third = (TextView) rowView.findViewById(R.id.dataValueString);

            first.setText("Item tab1 " + data.get(position).getIntValue());
            second.setText(String.valueOf(data.get(position).getIntValue()));
            third.setText(data.get(position).getStringValue());
        } else {
            rowView = inflater.inflate(R.layout.mainlistdata, parent, false);
            TextView first = (TextView) rowView.findViewById(R.id.dataTitle);
            TextView second = (TextView) rowView.findViewById(R.id.dataValue);
            TextView third = (TextView) rowView.findViewById(R.id.dataValueString);

            first.setText("Item tab2 " + data.get(position).getIntValue());
            second.setText(String.valueOf(data.get(position).getIntValue()));
            third.setText(data.get(position).getStringValue());
        }
        return rowView;
    }
}

As you can see on the next image, I'm loading the data and then I'm loading the tabs. The problem is that each tab is only loading the first row of the data. Any idea How can I solve this? enter image description here enter image description here

Upvotes: 2

Views: 1741

Answers (1)

user
user

Reputation: 87064

As Sherif elKhatib already said in his comment having a ListView in another ListView should be avoided. The solution for your problems is using a single ListView along with different row types to give the illusion of having tabs in the ListView containing other ListViews, all handled by a custom adapter. Based on your example, you would have four row types:

  • normal row(the ones with the letters)
  • one row containing the tabs
  • two row types for each of the pseudo ListViews(unless your tabs should have the same row appearance in which case you would have a single tab row type)

To implement these row types you would need to use the getItemViewType() and getViewTypeCount() method of an adapter:

// different row types
private static final int NORMAL_ROW = 0;
private static final int TAB_ROW = 1;
private static final int TAB1_TYPE = 2;
private static final int TAB2_TYPE = 3;

@Override
public int getItemViewType(int position) {
    if (position < mData.size()) {
            // we are in the initial list's range so it's a normal row
        return NORMAL_ROW;
    } else if (position == mData.size()) {
        // we are at the tab level, so it's a tab row
        return TAB_ROW;
    } else {
        // based on the current selected tab(0 by default) we have
        // either a tab 1 row type or tab 2 row type
        return mCurrentSelectedTab == 0 ? TAB1_TYPE : TAB2_TYPE;
    }

}

@Override
public int getViewTypeCount() {
    // if your list-tabs need different row types then you'll need to
    // make the calculations here and update appropriately
    return 4;
}

In the getView() you would inflate the proper layout(your layouts) based on those row layout types and also set the data. The adapter will also need to handle the user selecting a tab to use notifyDataSetChanged() and update the ListView with the proper row type(and data) for the tab part of it.

You can see the whole sample here, also have a look at the xml layouts used, it should be quite easy to understand.

Upvotes: 2

Related Questions