Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Inflating different .XML layouts for objects.

Hello.

I have gone under a lot of frustration all because of this one issue I'm having: how do I inflate different .XML files for different objects in a ListView? I have done as much as I can do, such as making a ListItemType object:

public class ListItemType {
    public static int NOTE_VIEW = 0;
    public static int CATEGORY_VIEW = 1;
}

And a ListViewItem object:

public class ListViewItem {
    private int mType;
    private Object mObject;

    public ListViewItem(int type, Object object) {
        mType = type;
        mObject = object;
    }

    public int getType() {
        return mType;
    }

    public void setType(int type) {
        mType = type;
    }

    public Object getObject() {
        return mObject;
    }

    public void setObject(Object object) {
        mObject = object;
    }
}

I'm clueless as to where I go from here.

Please help.

1 Answer

David Anton
PLUS
David Anton
Courses Plus Student 30,936 Points

RecyclerView Implement

First of all you have to add/create to/your Model type that indicates the your Model type and to know which layout to inflate:

MyModel.java

    public class MyModel {
        public enum ModelTypes {
            TYPE_1,
            TYPE_2
        }
        // your model members ^^^

        ModelTypes type;
    }

Now you have to create your ViewHolder, I suggest to create a parent ViewHolder class and extends your model types with sub classes.

MyHolder.java

    public class MyHolder extends RecyclerView.ViewHolder {

        // must have the ViewHolder default constructor
        public MyHolder(View itemView) {
            super(itemView);
        }

        public static MyHolder inflateViewByType(MyModel.ModelTypes type, 
                                                LayoutInflater layoutInflater, ViewGroup parent) {
            View view;
            switch (type) {
                case TYPE_1:
                    view = layoutInflater.inflate(R.layout.layout_type_1, parent, false);
                    return new MyHolderType1(view);
                case TYPE_2:
                    view = layoutInflater.inflate(R.layout.layout_type_2, parent, false);
                    return new MyHolderType2(view);
            }
            // Model type not supported
            return null;
        }
    }

Create your ViewHolder types, in my example i have only two types.

MyHolderType1.java

    public class MyHolderType1 extends MyHolder {

        // layout members

        public MyHolderType1(View itemView) {
            super(itemView);
            // init your layout members by for layout_type_1 by itemView.findViewById(...)
        }
    }

MyHolderType2.java

    public class MyHolderType2 extends MyHolder {

        // layout members

        public MyHolderType2(View itemView) {
            super(itemView);
            // init your layout members by for layout_type_2 by itemView.findViewById(...)
        }
    }

Until now you only create the ViewHolder types to implement the RecyclerView. RecyclerView must have an Adapter that extends the default RecyclerView.Adapter, to do this we have to create a new class and extends the RecyclerView.Adapter with our ViewHolder.

MyRecyclerViewAdapter.java

    public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyHolder> {

        private List<MyModel> mData;

        public MyRecyclerViewAdapter(@NonNull List<MyModel> data) {
            mData = data;
        }

        @Override
        public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
            MyModel.ModelTypes type = MyModel.ModelTypes.values()[viewType];
            return MyHolder.inflateViewByType(type, layoutInflater, parent);
        }

        @Override
        public void onBindViewHolder(MyHolder holder, int position) {

            MyModel model = mData.get(position);
            switch (model.type) {
                case TYPE_1:
                    setupViewType1((MyHolderType1) holder, model);
                    break;
                case TYPE_2:
                    setupViewType2((MyHolderType2) holder, model);
                    break;
            }
        }

        // to update the adapter data without reinitialize it
        public void updateData(@NonNull List<MyModel> newData) {
            mData = newData;
            notifyDataSetChanged();
        }

        private void setupViewType1(MyHolderType1 holder, MyModel model) {

            // do what you want with your views in layout_type_1
        }

        private void setupViewType2(MyHolderType2 holder, MyModel model) {

            // do what you want with your views in layout_type_2
        }

        @Override
        public int getItemCount() {
            return mData.size();
        }

        @Override
        public int getItemViewType(int position) {
            return mData.get(position).type.ordinal();
        }
    }

Keep going, we almost done. Now we have to initialize the RecyclerView in our activity, and set the an instance of MyRecyclerViewAdapter to it. To this we have find out RecyclerView in and xml by calling findViewById(...), and then set the LayoutManager to the RecyclerView, after all we set the adapter with our data.

MyActivity.java

    public class MyActivity extends Activity{

        // ....

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_layout);


            // init your views
            RecyclerView recyclerView = findViewById(R.id.recycler_view);


            // here you can pull or generate your recycler view list data
            List<MyModel> data = getData();
            MyRecyclerViewAdapter adapter = new MyRecyclerViewAdapter(data);


            // set layout manager to the recycler view (Required)
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, 
                                                            LinearLayoutManager.VERTICAL, false);
            recyclerView.setLayoutManager(linearLayoutManager);

            // set the adapter to the recycler view (Required)
            recyclerView.setAdapter(adapter);
        }

        // ....
    }
Seth Kroger
Seth Kroger
56,413 Points

While I see where you're going with this solution, isn't using a flag for type along with multiple switch/case statements to choose a method based on type brittle and error-prone? Why use this method when inheritance/polymorphism does the same task for free?

Like Seth said, I'm getting an error in my Holder class along with my MainActivity. The error for my Holder class tells me that the break statement is unreachable, and in my MainActivity it tells me that a class or interface is expected.

I'm getting this error now:

09-30 16:46:33.320 12592-12592/com.x.x E/AndroidRuntime: FATAL EXCEPTION: main
                                                                      Process: com.x.x, PID: 12592
                                                                      java.lang.NullPointerException: Attempt to read from field 'com.x.x.models.ObjectModel$ModelTypes com.x.x.models.ObjectModel.type' on a null object reference
                                                                          at com.x.x.adapters.ListAdapter.getItemViewType(ListAdapter.java:32)
                                                                          at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4664)
                                                                          at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617)
                                                                          at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)
                                                                          at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390)
                                                                          at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
                                                                          at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574)
                                                                          at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3028)
                                                                          at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2906)
                                                                          at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3283)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1077)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                                                                          at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1705)
                                                                          at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1559)
                                                                          at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                                                                          at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1705)
                                                                          at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1559)
                                                                          at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                                                                          at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                                                                          at android.view.View.layout(View.java:15654)
                                                                          at android.view.ViewGroup.layout(ViewGroup.java:4969)
                                                                          at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2102)
                                                                          at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1859)
                                                                          at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1078)
                                                                          at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5875)
                                                                          at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
                                                                          at android.view.Choreographer.doCallbacks(Choreographer.java:580)
                                                                          at android.view.Choreographer.doFrame(Choreographer.java:550)
                                                                          at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
                                                                          at android.os.Handler.handleCallback(Handler.java:739)
                                                                          at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                          at android.os.Looper.loop(Looper.java:135)
                                                                          at android.app.ActivityThread.main(ActivityThread.java:5351)
                                                                          at java.lang.reflect.Method.invoke(Native Method)
                                                                          at java.lang.reflect.Method.invoke(Method.java:372)
                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)

The 'type' object in ObjectModel is null, not 100% sure how to fix that.

This is where it gives me the error:

@Override
    public int getItemViewType(int position) {
        return mList.get(position).type.ordinal();
    }