Recyclerview ViewHolder : onCreateViewHolder , view binding 和 onBindViewHolder 在同一个 class
Recyclerview ViewHolder : onCreateViewHolder , view binding and onBindViewHolder in the same class
在 RecyclerView.ViewHolder
中,视图被传递给构造函数。这个视图是一个膨胀的布局。 RecyclerView.ViewHolder 仅将视图与 findViewById
绑定。
RecyclerView.Adapter
的作用是:
- 膨胀
onCreateViewHolder
中的布局
- 将 ViewHolder 与数据集绑定
onBindViewHolder
我有几个 RecyclerView 显示相同的数据列表。我希望每个 RecyclerView 与其各自的 ViewHolder 显示不同。我的目标是为每个 RecyclerView 使用相同的通用 RecyclerView.Adapter。
因此必须将 ViewHolder 传递给此 RecyclerView.Adapter。
我正在尝试实现一个可以实现所有 3 种方法的 ViewHolder。
知道如何实现吗?
我看了不同的项目。迄今为止最好的,AdapterDelegates绕过了这个问题。但是您仍然必须处理 AdapterDelegate 和 ViewHolder classes 。如何将两者组合在同一个 class 中? (没有内部 class )
您可以通过将 R.layout 传递给您的 RVadapter
static int layoutResource;
public RVadapter(Context context, int id) {
layoutResource = id;
}
然后您可以在 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);
}
我建议创建一个抽象的 ViewHolder 父级 Class
。它应该有一个静态实例化方法和一个 bindViewHolder
方法。设计 Adapter 构造函数以接受 ViewHolder 父对象 Class
。使用时,传递子Class
对象,在onCreateViewHolder
中,使用Reflection创建子ViewHolder。当你得到一个 onBindViewHolder 时,将它传递给 ViewHolder 即可。
这是一个工作示例。我测试了它,它起作用了。我已经删除了非必要的代码。
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();
}
}
正如承诺的那样,我创建了一个不使用反射的新答案。它在技术上使用两个 classes(工厂 class 和持有者 class),而不是一个,但完全相同。代码已经过测试,可以正常工作。
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);
}
}
在 RecyclerView.ViewHolder
中,视图被传递给构造函数。这个视图是一个膨胀的布局。 RecyclerView.ViewHolder 仅将视图与 findViewById
绑定。
RecyclerView.Adapter
的作用是:
- 膨胀
onCreateViewHolder
中的布局 - 将 ViewHolder 与数据集绑定
onBindViewHolder
我有几个 RecyclerView 显示相同的数据列表。我希望每个 RecyclerView 与其各自的 ViewHolder 显示不同。我的目标是为每个 RecyclerView 使用相同的通用 RecyclerView.Adapter。 因此必须将 ViewHolder 传递给此 RecyclerView.Adapter。 我正在尝试实现一个可以实现所有 3 种方法的 ViewHolder。 知道如何实现吗?
我看了不同的项目。迄今为止最好的,AdapterDelegates绕过了这个问题。但是您仍然必须处理 AdapterDelegate 和 ViewHolder classes 。如何将两者组合在同一个 class 中? (没有内部 class )
您可以通过将 R.layout 传递给您的 RVadapter
static int layoutResource;
public RVadapter(Context context, int id) {
layoutResource = id;
}
然后您可以在 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);
}
我建议创建一个抽象的 ViewHolder 父级 Class
。它应该有一个静态实例化方法和一个 bindViewHolder
方法。设计 Adapter 构造函数以接受 ViewHolder 父对象 Class
。使用时,传递子Class
对象,在onCreateViewHolder
中,使用Reflection创建子ViewHolder。当你得到一个 onBindViewHolder 时,将它传递给 ViewHolder 即可。
这是一个工作示例。我测试了它,它起作用了。我已经删除了非必要的代码。
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();
}
}
正如承诺的那样,我创建了一个不使用反射的新答案。它在技术上使用两个 classes(工厂 class 和持有者 class),而不是一个,但完全相同。代码已经过测试,可以正常工作。
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);
}
}