我正在尝试在我的应用程序中实现搜索功能,用户可以在其中搜索不同类型的数据:曲目,专辑或艺术家。用户在活动A中选择要搜索的数据类型,然后在活动B中执行实际搜索。
活动A内:
Intent intent = new Intent(this, PostSearch.class);
intent.putExtra(PostSearch.EXTRA_POST_TYPE, postType);
startActivityForResult(intent, RC_NEW_POST);
EXTRA_POST_TYPE
指定将执行的搜索类型。然后在活动B中提取了这些额外资源。我已经为每种数据类型定义了类:Track
,Album
和Artist
。在活动B中,我有一个由适配器支持的RecyclerView
。我希望为每种数据类型使用相同的适配器定义。但是,我不确定是否可行。
我对适配器的定义如下(为简便起见,简称:)
public class PostSearchAdapter extends RecyclerView.Adapter<PostSearchAdapter.PostSearchViewHolder> {
private Context mContext;
private int mSearchType;
private List<?> mSearchResults;
public PostSearchAdapter(Context context, int searchType) {
this.mContext = context;
mSearchType = searchType;
switch (mSearchType) {
case 0:
mSearchResults = new ArrayList<Track>();
break;
case 1:
mSearchResults = new ArrayList<Album>();
break;
case 2:
mSearchResults = new ArrayList<Album>();
break;
default:
Log.i(TAG, "Invalid search type for adapter. Uh oh...");
break;
}
}
@Override
public int getItemCount() {
if (mSearchResults == null) {
return 0;
} else {
return mSearchResults.size();
}
}
@NonNull
@Override
public PostSearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View searchResult = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.list_item_search_result, parent, false);
return new PostSearchViewHolder(searchResult);
}
@Override
public void onBindViewHolder(PostSearchViewHolder viewHolder, int position) {
switch (mSearchType) {
case 0:
Track track = (Track) mSearchResults.get(position);
viewHolder.mTitle.setText(track.getTitle());
viewHolder.mArtist.setText(track.getArtistNames());
if (!TextUtils.isEmpty(track.getAlbum().getLargeAlbumCover())) {
Picasso.get()
.load(track.getAlbum().getLargeAlbumCover())
.error(R.drawable.ic_no_cover)
.into(viewHolder.mCover);
}
break;
case 1:
// Handle album results here
break;
case 2:
// Handle artist results here
break;
default:
break;
}
}
public void addData(List<?> newData) {
mSearchResults.add(newData);
notifyDataSetChanged();
}
public void clearData(){
mSearchResults.clear();
}
}
我以这种方式定义了适配器,因为当我在活动B中实例化适配器时,我将从EXTRA_POST_TYPE
中知道要处理的数据类型,并且可以执行以下操作:
mAdapter = new PostSearchAdapter(this, mSearchType);
...其中mSearchType
具有EXTRA_POST_TYPE
存储的值。但是,我的问题现在必须在适配器中处理mSearchResults
。我已经使用Java ?
通配符类型定义了这个成员变量:
private List<?> mSearchResults;
我这样做是因为在实例化适配器之前,我不知道列表将包含哪种类型的数据。然后,我在适配器的重写构造函数中实例化mSearchResults
,因为我现在基于mSearchType
的值知道要放入数组的数据类型:
public PostSearchAdapter(Context context, int searchType) {
this.mContext = context;
mSearchType = searchType;
switch (mSearchType) {
//Instantiate mSearchResults here
}
}
我面临着两个大问题。首先是我仍然必须为onBindViewHolder()
中的列表中存储的对象强制转换:
Track track = (Track) mSearchResults.get(position);
我知道这是一个问题,因为使用参数化数据类型的主要优点之一是,我们不再需要像上面那样进行转换。其次,我仍然在addData()
中遇到编译时错误。 Android Studio抱怨捕获和通配符有一些问题:
我不完全理解错误,这使我陷入了停顿。
我应该提一下,我对Java中的泛型没有最全面的了解,但是我觉得我试图解决的问题可以通过使用它们来解决。任何对此的见解将不胜感激!
答案 0 :(得分:2)
您没有正确使用泛型。
您的Track
和Album
共享超类或任何接口吗?
如果不创建接口并将其添加到两个类中,否则请继续使用您的超类:
public interface ISearchResult{ }
然后将您的适配器更改为abstract
类,并为ViewHolder
添加适当的类型声明和模板:
public abstract class PostSearchAdapter<T extends ISearchResult> extends RecyclerView.Adapter<PostSearchAdapter.PostSearchViewHolder> {
// note change from private to protected
protected final Context mContext;
protected final ArrayList<T> mSearchResults = new ArrayList<T>();
// constructor no longer handles weird type variable
public PostSearchAdapter(Context context) {
this.mContext = context;
}
// oncreate viewholder is removed on purpose
@Override
public void onBindViewHolder(PostSearchAdapter.PostSearchViewHolder viewHolder, int position) {
viewHolder.bind(mSearchResults.get(position));
}
public void addData(List<T> newData) {
mSearchResults.addAll(newData);
notifyDataSetChanged();
}
public void clearData(){
mSearchResults.clear();
}
@Override
public int getItemCount() {
return mSearchResults.size(); // this is never null
}
// ViewHolder template
protected abstract class PostSearchViewHolder extends RecyclerView.ViewHolder{
protected PostSearchViewHolder(View itemView) {
super(itemView);
// this can have some or all views that are shared between search results
}
protected abstract void bind(T data);
}
}
这涉及基本方法。现在,您需要为每种搜索结果类型创建此适配器的子类(请注意,extends中使用了type参数):
public class TrackAdapter extends PostSearchAdapter<Track> {
public TrackAdapter(Context context) {
super(context);
}
@NonNull
@Override
public PostSearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// if all types share layout You can create helper method in superclass to inflate it
View searchResult = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.list_item_search_result, parent, false);
return new TrackViewHolder(searchResult);
}
protected class TrackViewHolder extends PostSearchViewHolder{
protected TrackViewHolder(View itemView) {
super(itemView);
// store views not included in superclass in fields
}
@Override
protected void bind(Track data) {
// bind views to Track data...
}
}
}
现在只剩下创造力了。可以使用简单的静态工厂方法代替它,例如,将其添加到PostSearchAdapter
类中:
public static PostSearchAdapter create(Context context, int resultType){
switch (resultType){
case 0:
return new TrackAdapter(context);
case 1:
return new AlbumAdapter(context);
default:
throw new IllegalArgumentException("Invalid resultType:"+resultType);
}
}
这使您可以通过简单的调用获得正确类型的适配器:
mAdapter = PostSearchAdapter.create(this, mSearchType);
答案 1 :(得分:0)
编辑
甚至不能将元素1加1也行。 我附加的2个链接中已详细说明了原因,请查看。这也不起作用:
List<?> list = new ArrayList<>();
List<?> list2 = new ArrayList<>();
list.add(list2.get(0)); // I thought for-each loop would work, this proves that it wouldn't
因为类型可能不同。甚至无法从同一列表中获取元素:
List<?> list = new ArrayList<>();
list.add(list.get(0)); // Same compile error
我认为使用addAll
方法是可行的。但是它显示了相同的错误。
这可以编译,但是如果可以正常工作,那就不知道了:
List list = new ArrayList<>();
List<?> list2 = new ArrayList<>();
list.addAll(list2);