首页 文章

RecyclerView.Adapter用于多种数据类型

提问于
浏览
1

我正在尝试在我的应用中实现搜索功能,用户可以在其中搜索不同类型的数据:曲目,专辑或艺术家 . 用户选择他们将在活动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中提取此额外内容 . 我已经为每种数据类型定义了类: TrackAlbumArtist . 在活动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();
    }    
}

我_243436_与 EXTRA_POST_TYPE 打交道并且可以做类似的事情:

mAdapter = new PostSearchAdapter(this, mSearchType);

...其中 mSearchType 具有 EXTRA_POST_TYPE 存储的值 . 但是,我的问题现在必须在我的适配器中处理 mSearchResults . 我使用Java ? 通配符类型定义了这个成员变量:

private List<?> mSearchResults;

在知道适配器实例化之前,我知道列表将包含哪种类型的数据 . 然后我在适配器的overriden构造函数中实例化 mSearchResults ,因为我现在知道基于 mSearchType 的值放入数组的数据类型:

public PostSearchAdapter(Context context, int searchType) {
    this.mContext = context;
    mSearchType = searchType;

    switch (mSearchType) {
        //Instantiate mSearchResults here
    }
}

有's 2 big issues that I'米面对这个 . 第一个是我仍然必须为 onBindViewHolder() 中列表中存储的对象进行强制转换:

Track track = (Track) mSearchResults.get(position);

我知道这是一个问题,因为使用参数化数据类型的一个主要优点是我们不再需要像上面那样进行构建 . 其次,我在 addData() 内仍然遇到编译时错误 . Android Studio通过捕获和通配符来抱怨:

enter image description here

我不完全理解错误,这让我处于停滞状态 .

我应该提一下,我对Java中的泛型没有最全面的理解,但我觉得我试图解决的问题可以通过使用它们来完成 . 任何形式的洞察力将不胜感激!

2 回答

  • 0

    EDIT

    甚至不添加1乘1的元素也行 . 原因在 the 2 links I attached 中详细解释,检查出来 . 这也不起作用:

    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);
    

    在深入挖掘后,我发现thisthis可能有助于我们更好地理解 .

  • 1

    你没有正确使用泛型 .

    你的 TrackAlbum 共享一个超类或任何接口吗?

    如果没有创建接口并将其添加到两个类,否则继续使用您的超类:

    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);
    

相关问题