Use custom View in a RecyclerView Adapter?

自闭症网瘾萝莉.ら 提交于 2019-11-27 23:43:25

问题


I have a basic custom View which looks like this:

public class CustomView extends RelativeLayout {

    private User user;

    private ImageView profilePicture;

    public CustomView(Context context) {
        super(context);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        inflate(getContext(), R.layout.custom_layout, this);

        profilePicture = (ImageView) findViewById(R.id.profilePicture);

        // ACCESS USER MODEL HERE
        // e.g. user.getUsername()
    }

}

As you can see, I'd like to access user data in the View (i.e.: user.getUsername()).

I also need to be able to use the custom View in a RecyclerView Adapter.

Here is what my Adapter currently looks like:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private Context context;

    private List<User> userData;

    public MyAdapter(Context context, List<User> userData) {
        this.context = context;
        this.userData = userData;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);

        // HOW TO INFLATE THE CUSTOM VIEW?

        // ViewHolder viewHolder = new ViewHolder(customView);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // ANYTHING HERE?
    }

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

}

How can I inflate the custom View in the Adapter?
Also, should I put anything in onBindViewHolder()?

Note: I must use a custom View, as I use this View under different Adapters (i.e.: not just this RecyclerView Adapter).


回答1:


Assuming a CustomView class that looks something like this:

public class CustomView extends RelativeLayout {
    private User user;
    private ImageView profilePicture;

    // override all constructors to ensure custom logic runs in all cases
    public CustomView(Context context) {
        this(context, null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }
    public CustomView(
            Context context,
            AttributeSet attrs,
            int defStyleAttr,
            int defStyleRes
    ) {
        super(context, attrs, defStyleAttr, defStyleRes);

        // put all custom logic in this constructor, which always runs
        inflate(getContext(), R.layout.custom_layout, this);
        profilePicture = (ImageView) findViewById(R.id.profilePicture);
    }

    public void setUser(User newUser) {
        user = newUser;
        // ACCESS USER MODEL HERE
        // e.g. user.getUsername()
    }
}

Your RecyclerView.Adapter and RecyclerView.ViewHolder could look something like this:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    // no Context reference needed—can get it from a ViewGroup parameter
    private List<User> userData;

    public MyAdapter(List<User> userData) {
        // make own copy of the list so it can't be edited externally
        this.userData = new ArrayList<User>(userData);
    }

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

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // no need for a LayoutInflater instance—
        // the custom view inflates itself
        CustomView itemView = new CustomView(parent.getContext());
        // manually set the CustomView's size
        itemView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        ));
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.getCustomView().setUser(userData.get(position));
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        private CustomView customView;

        public ViewHolder(View v) {
            super(v);
            customView = (CustomView) v;
        }

        public CustomView getCustomView() {
            return customView;
        }
    }
}
  • The CustomView manages its own setup, which occurs in its own constructor and in this case uses inflation of an XML file. (Alternatively, it could set up its child views programmatically.)
  • Because of this, the RecyclerView.Adapter doesn't need to perform any inflation—it just creates a new CustomView instance, and lets the CustomView worry about its own setup.
  • The CustomView can't get a User instance until its setUser method is called, so user access cannot occur in the constructor. In any case, over one CustomView lifetime, a RecyclerView could ask it to show information for many different users at different times. The CustomView needs to be able to do this. Therefore, a setUser method is introduced.
  • Because the CustomView is instantiated by code instead of by XML, attributes for size can't be defined in XML. Therefore, sizing is done programmatically after instantation.
  • onBindViewHolder simply calls setUser on the CustomView to link the CustomView with the correct User instance.
  • The ViewHolder class is now just a link between a RecyclerView item and a CustomView.

Using pre-built custom views from another class within RecyclerViews (i.e. not inflating XML within the RecyclerView.Adapter) never seems to be discussed. I think it's an excellent idea even when the custom view is exclusively used within a RecyclerView, because it promotes separation of concerns and adherence to the Single Responsibility Principle.




回答2:


CustomView extends RelativeLayout {

You have a View already (well, a ViewGroup)

HOW TO INFLATE THE CUSTOM VIEW?

You don't need to... The point of a Custom View object is to not need XML, therefore no inflation.

You can create new CustomView(), but you need to set all the layout parameters, which looks cleaner in XML, I think.


Most RecyclerView tutorials show inflating via XML though.

View customView = inflater.inflate(...);
ViewHolder viewHolder = new ViewHolder(customView);

That should work because in the class chain, you have CustomView > RelativeLayout > ViewGroup > View

LayoutInflater inflater = LayoutInflater.from(context);

Like I said before, you don't need this if there is no XML file you want to inflate.

You also don't need the context variable.

parent.getContext() is a fine solution.

// ANYTHING HERE?

Well, yeah, you should "bind" the ViewHolder with the data that the ViewHolder should hold.

Again, most, if not all, tutorials cover this.




回答3:


list_content.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">

    <TextView
        android:text="TextView"
        android:layout_width="@dimen/_160sdp"
        android:textSize="@dimen/_14sdp"
        android:layout_gravity="center|bottom"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:paddingTop="@dimen/_10sdp"
        android:id="@+id/name"
         />

    <ImageView
        android:layout_width="@dimen/_20sdp"
        android:layout_gravity="center|right"
        android:layout_height="@dimen/_20sdp"
        app:srcCompat="@drawable/close"
        android:id="@+id/close"
        android:layout_weight="1" />
</LinearLayout>

Adapter.java

public class yourAdapter extends RecyclerView.Adapter<yourAdapter .SimpleViewHolder> {


private Context mContext;
ArrayList<String> mylist;

public yourAdapter (Context context, ArrayList<String> checklist) {
    mContext = context;
    mylist = checklist;
}

@Override
public yourAdapter .SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_content, parent, false);
    return new yourAdapter .SimpleViewHolder(view);
}
@Override
public void onBindViewHolder(final yourAdapter .SimpleViewHolder holder, final int position) {
    holder.name.setText(adap.getName());


}

@Override
public long getItemId(int i) {
    return 0;
}

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

@Override
public int getItemViewType(int i) {
    return 0;
}

public static class SimpleViewHolder extends RecyclerView.ViewHolder {
    TextView name;
    Imageview content;


    public SimpleViewHolder(View itemView) {
        super(itemView);
        name= (TextView) itemView.findViewById(R.id.name);
        close= (Imageview) itemView.findViewById(R.id.close);

    }
}

}

Then call the adapter class in activity where you want

https://developer.sonymobile.com/2010/05/20/android-tutorial-making-your-own-3d-list-part-1/



来源:https://stackoverflow.com/questions/42522121/use-custom-view-in-a-recyclerview-adapter

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