问题
In RecyclerView.ViewHolder
, a view is passed to the constructor. This view is an inflated layout. The RecyclerView.ViewHolder only bind the views with findViewById
.
The RecyclerView.Adapter
has the role to :
- inflate the layout in
onCreateViewHolder
- bind the ViewHolder with the data set with
onBindViewHolder
I have several RecyclerViews displaying the same list of data. I want each RecyclerView to display differently with their respective ViewHolder. My goal is to use the same generic RecyclerView.Adapter for each RecyclerView. So the ViewHolder has to be passed to this RecyclerView.Adapter. I'm trying to implement a ViewHolder that can implement all 3 methods. Any idea how to achieve this ?
I looked at different projects. The best so far, AdapterDelegates circumvents this problem. But you still have to deal with AdapterDelegate and ViewHolder classes . How to combine both in the same class ? ( without inner class )
回答1:
As promised I have created a new answer, which does not use reflection. It technically uses two classes (a factory class and a holder class), not one, but is just the same. The code is tested, and it works.
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>
{
List<Object> _data;
MyViewHolder.Factory _factory;
MyAdapter(List data, MyViewHolder.Factory factory)
{
_data = data;
_factory = factory;
if (_data == null || _factory == null)
{
throw new NullPointerException("Both data and factory cannot be null!");
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return _factory.createViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
holder.bindViewHolder(_data.get(position));
}
@Override
public int getItemCount()
{
return _data.size();
}
}
MyViewHolder.java
public abstract class MyViewHolder extends RecyclerView.ViewHolder
{
public interface Factory
{
public abstract MyViewHolder createViewHolder(ViewGroup parent, int viewType);
}
public MyViewHolder(View itemView)
{
super(itemView);
}
public abstract void bindViewHolder(Object data);
}
MainActivity.java
public class MainActivity extends AppCompatActivity
{
static class NameViewHolder extends MyViewHolder
{
// If preferred, the following can be created anonymously in code
static class Factory implements MyViewHolder.Factory
{
@Override
public MyViewHolder createViewHolder(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_recycler_item, parent, false);
return new NameViewHolder(v);
}
};
TextView tv;
NameViewHolder(View v)
{
super(v);
tv = (TextView)v.findViewById(R.id.textView);
}
@Override
public void bindViewHolder(Object data)
{
tv.setText((String)data);
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
RecyclerView rv = (RecyclerView)findViewById(R.id.my_recycler_view);
rv.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
rv.setLayoutManager(layoutManager);
ArrayList<String> data = new ArrayList();
for (int i = 1; i < 100; ++i)
data.add(Integer.toString(1000 + i));
MyAdapter adapter = new MyAdapter(data, new NameViewHolder.Factory());
rv.setAdapter(adapter);
}
}
回答2:
I would suggest creating an abstract ViewHolder parent Class
. It should have a static instantate method and a bindViewHolder
method. Design the Adapter constructor to accept the ViewHolder parent Class
object. When used, pass the child Class
Object, and in onCreateViewHolder
, use Reflection to create the child ViewHolder. When you get an onBindViewHolder, just pass it to the ViewHolder.
Here is a working example. I tested it, and it worked. I have removed non-essential code.
MainActivity.java
public class MainActivity extends AppCompatActivity
{
static class NameViewHolder extends MyViewHolder
{
TextView tv;
public static MyViewHolder instantate(ViewGroup parent, int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.main_recycler_item, parent, false);
MyViewHolder vh = new NameViewHolder(v);
return vh;
}
NameViewHolder(View v)
{
super(v);
tv = (TextView)v.findViewById(R.id.textView);
}
@Override
public void bindViewHolder(Object data)
{
tv.setText((String)data);
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView)findViewById(R.id.my_recycler_view);
rv.setHasFixedSize(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
rv.setLayoutManager(layoutManager);
ArrayList<String> data = new ArrayList();
for (int i = 1; i < 100; ++i)
data.add(Integer.toString(1000 + i));
MyAdapter adapter = new MyAdapter(data, NameViewHolder.class);
rv.setAdapter(adapter);
}
}
MyViewHolder.java
public abstract class MyViewHolder extends RecyclerView.ViewHolder
{
// The following has to be declared in sub class. As Java 7 does not support static interface, we commented it out here
//public static MyViewHolder instantate(ViewGroup parent, int viewType);
public MyViewHolder(View itemView)
{
super(itemView);
}
public abstract void bindViewHolder(Object data);
}
MyAdapter.java
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder>
{
List<Object> _data;
Class _holderClass;
MyAdapter(List data, Class holderClass)
{
_data = data;
_holderClass = holderClass;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
MyViewHolder vh = null;
try
{
Class[] cArg = {ViewGroup.class, int.class};
Method instantateMethod = _holderClass.getMethod("instantate", cArg);
vh = (MyViewHolder) instantateMethod.invoke(null, parent, viewType);
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}
catch (InvocationTargetException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
return vh;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
holder.bindViewHolder(_data.get(position));
}
@Override
public int getItemCount()
{
return _data.size();
}
}
回答3:
You can by making passing R.layout to your RVadapter
static int layoutResource;
public RVadapter(Context context, int id) {
layoutResource = id;
}
then you can do this in your ViewHolder
if(RVadapter.layoutResource == R.layout.message_layout) {
message_view = (CardView) itemView.findViewById(R.id.card_message);
message_image = (ImageView) itemView.findViewById(R.id.message_image);
message_name = (TextView) itemView.findViewById(R.id.messenger);
message_details = (TextView) itemView.findViewById(R.id.details);
}
else if(RVadapter.layoutResource == R.layout.guide_layout){
guide_name = (TextView) itemView.findViewById(R.id.new_travel_name);
guide_gendr_age = (TextView) itemView.findViewById(R.id.new_travel_gender_age);
guide_tours = (TextView) itemView.findViewById(R.id.new_travel_tour);
rb = (RatingBar) itemView.findViewById(R.id.new_travel_rb);
}
来源:https://stackoverflow.com/questions/39238674/recyclerview-viewholder-oncreateviewholder-view-binding-and-onbindviewholder