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 RecyclerView
s (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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…