How to use ExpandableRecyclerView library with multiple (child) viewTypes?

我只是一个虾纸丫 提交于 2019-12-24 16:19:08

问题


I need an Activity with an expandable RecyclerView like the one in this picture:

So I am using this third party library project . That part works.

The problem arose when I did what I am describing in the next two paragraphs:

My next requirement is that I want different child-rows for different parent-rows. I am reproducing (if that's the right word) this example to create multiple child-viewholders.

Description of Example: The idea is basically to have the different Child-viewholders (corresponding to each child-row style) extending one common childview-holder, and then inside our ExpandableRecyclerAdapter where we are passed the ArrayList of the data displayed in parent-rows in the constructor (parentItemList in the SSCCE), we declare constants representing all the child-row-types (TYPE_EDITTEXT and TYPE_SPINNER in the SSCCE below); and then inside getItemViewType(int position) we compare the data item from parentItemList using the passed int position as the index, with each text string on parent-rows and in each case, assign the viewType int variable a TYPE constant, and the viewType.Then this int viewType is passed to the onCreateChildViewHolder and onBindChildViewHolder, so we execute another switch-block in the definitions of each of these methods and then in onCreateChildViewHolder we return a corresponding ChildViewHolder (inflated from a corresponding layout resource) (either R.layout.custom_row_child_with_edittext or R.layout.custom_row_child_with_spinner in the SSCCE) ; and in onBindChildViewHolder, update the data according to the case in the switch-block.

The problem is the dollowing ClassCastException:

07-26 18:21:54.380: E/AndroidRuntime(276): FATAL EXCEPTION: main
07-26 18:21:54.380: E/AndroidRuntime(276): java.lang.ClassCastException: tests.test.epmc_mobile.search_module.no_ui.expandable_recycler_view.EPMCChildViewHolder
07-26 18:21:54.380: E/AndroidRuntime(276):      at com.bignerdranch.expandablerecyclerview.Adapter.ExpandableRecyclerAdapter.onBindViewHolder(ExpandableRecyclerAdapter.java:144)

The thing is that ExpandableRecyclerAdapter.java is a part of the third party library project, and Line#144 (and 142 and 143 which I added) are:

Log.i(TAG, "+++++++++++++++++++++++++THE TYPE OF THE RecyclerView.ViewHolder WHICH HAS BEEN PASSED AS AN ARGUMENT IS "
                    + holder.getClass().getSimpleName() + "++++++++++++++++++++++++++++");
            PVH parentViewHolder = (PVH) holder;

Before I tried to do what I am doing above, it was working fine.


MainActivity.java

public class MainActivity extends FragmentActivity {
    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private static int [] imageIds = {R.drawable.ic_action_call, R.drawable.ic_action_copy, R.drawable.ic_action_discard};
    private static String [] titles = {"Dummy Text One", "Dummy Text Two", "Dummy Text Three"};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "onCreate of MainActivity called.");//check
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView) findViewById(R.id.mainActivity_recyclerView);

        MyExpandableRecyclerAdapter myExpandableRecyclerAdapter = new MyExpandableRecyclerAdapter(this, populateDataList(this));
        recyclerView.setAdapter(myExpandableRecyclerAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this)); 
    }

    private ArrayList<ParentObject> populateDataList(Context context) {
        Log.i(TAG, "populateDataList of MainActivity called.");//check
        ArrayList<ParentObject> parentObjectList = new ArrayList<>();
        for (int i=0; i<imageIds.length && i<titles.length; i++) {
            MyCustomParentObject myCustomParentObject = new MyCustomParentObject(context);
            myCustomParentObject.setImageId(imageIds[i]);
            myCustomParentObject.setTitle(titles[i]);
            parentObjectList.add(myCustomParentObject);
        } 
        return parentObjectList;
    }
}

MyExpandableRecyclerAdapter.java

public class MyExpandableRecyclerAdapter extends ExpandableRecyclerAdapter<ParentViewHolder, ChildViewHolder> {
    private static final String TAG = MyExpandableRecyclerAdapter.class.getSimpleName();
    LayoutInflater layoutInflater;
    Context context;
    private List<ParentObject> parentItemList = new ArrayList<>();

    private static final int TYPE_EDITTEXT = 0;
    private static final int TYPE_SPINNER = 1;


    public MyExpandableRecyclerAdapter(Context context, List<ParentObject> parentObjectItemsList) {
        super(context, parentObjectItemsList);
        Log.i(TAG, "Constructor of MyExpandableRecyclerAdapter called.");//check
        layoutInflater = LayoutInflater.from(context);
        this.context = context;
        parentItemList = parentObjectItemsList;
    }



    @Override
    public int getItemViewType(int position) {
        int viewType;
        if (parentItemList.get(position).equals("EditText Entry")) {
            viewType = TYPE_EDITTEXT;
        } else {
            viewType = TYPE_SPINNER;
        }
        return viewType; 
    }




    @Override
    public MyParentViewHolder onCreateParentViewHolder(ViewGroup container, int viewType) {
        Log.i(TAG, "onCreateParentViewHolder of MyExpandableRecyclerAdapter called.");//check
        Log.i(TAG, "IN onCreateParentViewHolder, THE TYPE OF AN ITEM IN THE parentItemList IS " + parentItemList.get(1));
        return new MyParentViewHolder(layoutInflater.inflate(R.layout.custom_row_parent, container, false));
    }



    @Override
    public MyChildViewHolder onCreateChildViewHolder(ViewGroup container, int viewType) {
        Log.i(TAG, "onCreateChildViewHolder of MyExpandableRecyclerAdapter called.");//check
        switch(viewType) {
        case TYPE_EDITTEXT:
            return new MyChildViewHolder(layoutInflater.inflate(R.layout.custom_row_child_with_edittext, container, false), context);
        case TYPE_SPINNER:
            return new MyChildViewHolder(layoutInflater.inflate(R.layout.custom_row_child_with_spinner, container, false), context);
        default:
            return new MyChildViewHolder(layoutInflater.inflate(R.layout.custom_row_child_with_edittext, container, false), context);
        }
    }



    @Override
    public void onBindParentViewHolder(ParentViewHolder parentViewHolder, int position, Object parentObject) {
        Log.i(TAG, "onBindParentViewHolder of MyExpandableRecyclerAdapter called.");//check
        MyParentViewHolder myParentViewHolder = (MyParentViewHolder) parentViewHolder;
        MyCustomParentObject myCustomParentObject = (MyCustomParentObject) parentObject;
        myParentViewHolder.textView.setText(myCustomParentObject.getTitle());
        myParentViewHolder.imageView.setImageResource(myCustomParentObject.getImageId()); 
    }



    @Override
    public void onBindChildViewHolder(ChildViewHolder childViewHolder, int position, Object childObject) {
        Log.i(TAG, "onBindChildViewHolder of MyExpandableRecyclerAdapter called.");//check

        switch(childViewHolder.getItemViewType()) {
        case TYPE_EDITTEXT:
            MyChildViewHolderWithEditText myChildViewHolderWithEditText = (MyChildViewHolderWithEditText) childViewHolder;
            myChildViewHolderWithEditText.textView.setText("TextView " + (position+1) + " Title"); 
        case TYPE_SPINNER:
            MyCustomChildObject myCustomChildObject = (MyCustomChildObject) childObject;
            ArrayAdapter arrayAdapter = new ArrayAdapter(context, android.R.layout.simple_list_item_1, myCustomChildObject.getSpinnerItems());
            arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            MyChildViewHolderWithSpinner myChildViewHolderWithSpinner = (MyChildViewHolderWithSpinner) childViewHolder;
            myChildViewHolderWithSpinner.spinner.setAdapter(arrayAdapter);
        }
    }

}

MyCustomParentObject.java

public class MyCustomParentObject implements ParentObject {
    private static final String TAG = MyCustomParentObject.class.getSimpleName();
    //List to store all the children of the parent object in.
    private List<Object> childObjectList;
    private String [] bib;

    MyCustomParentObject (Context context) {
        super();
        bib = context.getResources().getStringArray(R.array.spinner_options);
    }


    @Override
    public List<Object> getChildObjectList() {
        Log.i(TAG, "getChildObjectList of MyCustomParentObject called.");//check
        //"You can either return a newly created list of children here or attach them later"
        return populateChildObjectList();
    }

    @Override
    public void setChildObjectList(List<Object> childObjectList) {
        Log.i(TAG, "setChildObjectList of MyCustomParentObject called.");//check
        childObjectList = childObjectList;
    }

    private List<Object> populateChildObjectList() {
        Log.i(TAG, "populateChildObjectList of MyCustomParentObject called.");//check
        childObjectList = new ArrayList<>();
        MyCustomChildObject myCustomChildObject = new MyCustomChildObject();
        myCustomChildObject.setSpinnerItems(bib);
        Object myCustomChildObjectCasted = (Object) myCustomChildObject;
        childObjectList.add(myCustomChildObjectCasted);
        return childObjectList;
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private int imageId;
    private String title;
    public int getImageId() {
        Log.i(TAG, "getImageId of MyCustomParentObject called.");//check
        return imageId;
    }
    public void setImageId(int imageId) {
        Log.i(TAG, "setImageId of MyCustomParentObject called.");//check
        this.imageId = imageId;
    }
    public String getTitle() {
        Log.i(TAG, "getTitle of MyCustomParentObject called.");//check
        return title;
    }
    public void setTitle(String title) {
        Log.i(TAG, "setTitle of MyCustomParentObject called.");//check
        this.title = title;
    }

}

MyCustomChildObject.java

public class MyCustomChildObject {
    private static final String TAG = MyCustomParentObject.class.getSimpleName();

    private String [] spinnerItems;

    public String [] getSpinnerItems() {
        return spinnerItems;
    }

    public void setSpinnerItems(String [] spinnerItems) {
        this.spinnerItems = spinnerItems;
    }

}

MyCustomParentViewHolder.java

public class MyParentViewHolder extends ParentViewHolder {
    private static final String TAG = MyParentViewHolder.class.getSimpleName();
    TextView textView;
    ImageView imageView;

    public MyParentViewHolder(View itemView) {
        super(itemView);
        Log.i(TAG, "Constructor of MyParentViewHolder called.");//check
        textView = (TextView) itemView.findViewById(R.id.parentCustomRow_textView);
        imageView = (ImageView) itemView.findViewById(R.id.parentCustomRow_imageView);
    }

}

MyCustomChildViewHolder.java

public class MyChildViewHolder extends ChildViewHolder {
    private static final String TAG = MyChildViewHolder.class.getSimpleName();

    public MyChildViewHolder(View itemView, final Context context) {
        super(itemView);
        Log.i(TAG, "Constructor of MyChildViewHolder called.");// check
    }
}

MyCustomChildViewHolderWithEditText.java

public class MyChildViewHolderWithEditText extends MyChildViewHolder {
    EditText editText;
    TextView textView;

    public MyChildViewHolderWithEditText(View itemView, Context context) {
        super(itemView, context);

        textView = (TextView) itemView.findViewById(R.id.childViewHolderWithEditText_TextView);
        editText = (EditText) itemView.findViewById(R.id.childViewHolderWithEditText_editText);
    }

}

MyCustomChildViewHolderWithSpinner.java

public class MyChildViewHolderWithSpinner extends MyChildViewHolder {
    Spinner spinner;

    public MyChildViewHolderWithSpinner(View itemView, Context context) {
        super(itemView, context);

        spinner = (Spinner) itemView.findViewById(R.id.childViewHolderWithSpinner_spinner);
        //ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, R.array.childViewSpinnerFields, android.R.layout.simple_spinner_item);
        //adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        ///spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {}
            @Override
            public void onNothingSelected(AdapterView<?> parent) {}
        });
    }

}

回答1:


By comparing Line#13 of this stack-trace of the error free program, and this one of the program which results in the exception, and by looking at the onCreateViewHolder(ViewGroup viewGroup, int viewType) method definition on Line#117 of this ExpandableRecyclerAdapter.java class, from which I infer that onCreateViewHolder is being passed the wrong viewType parameter.

But I haven't yet been able to figure out why!


I think I have figured it out.

Actually the trick of TYPE_SOMETHING constants that I am using in MyExpandableRecyclerAdapter is the same one they have used in ExpandableRecyclerAdapter, i.e. in my MyExpandableRecyclerAdapter, 0 is the value of TYPE_EDITTEXT and 1 is the value of TYPE_SPINNER; whereas in their ExpandableRecyclerAdapter, which is extended by my MyExpandableRecyclerAdapter, 0 is the value of TYPE_PARENT and 1 is the value of TYPE_CHILD.

If the Android framework passes the same viewType parameter to onCreateViewHolder which is returned from getItemViewType(int position), it will pass 1 if it was TYPE_SPINNER in my MyExpandableRecyclerAdapter, but their ExpandableRecyclerAdapter will understand it as TYPE_CHILD, and thus call onCreateChildViewHolder.

So I think the whole thing I am trying to achieve can't be done. =(



来源:https://stackoverflow.com/questions/31648760/how-to-use-expandablerecyclerview-library-with-multiple-child-viewtypes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!