Как использовать один и тот же адаптер RecyclerView для разных макетов

Я разрабатываю приложение, которое сильно зависит от использования RecyclerView.

Мне действительно нужно знать, как использовать тот же RecyclerView для разных макетов элементов. Пример макетов:

1) Элемент списка с именем и описанием

2) Элемент списка с изображением и именем

Вы можете видеть, что они похожи, но с небольшими изменениями в макете.

Спасибо заранее!

Ответ 1

Поскольку элемент recyclerview в xml не зависит от типа элементов, которые вы будете накачивать в нем, вы можете продолжить и использовать один и тот же файл макета представления ресайклеров для трех фрагментов.

Что касается адаптеров, ваши списки кажутся однородными (т.е. один вид элемента вида). Лучше всего использовать три разных адаптера для каждого случая. Вы можете настроить конструктор, добавить пользовательские вспомогательные методы для каждого типа адаптера в удобное для вас время.

Теперь, если у вас есть гетерогенный список, вам придется переопределить getItemViewType() в вашем адаптере и использовать его соответствующим образом в onCreateViewHolder() и onBindViewHolder()

Надеюсь, это поможет!:)

Ответ 2

Я столкнулся с подобной ситуацией, и вот модель, которой я следовал.

Прежде всего, Файл макета фрагмента.

Файл макета фрагмента не изменится для всех 3 фрагментов (в основном это похоже на фрагмент списка), поэтому я создал файл шаблона для фрагмента списка.

list_fragment_template.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

Теперь фрагментарный код:

В моем случае все 3 фрагмента делают почти одинаковые вещи (получайте просмотр ресайклеров, получите адаптер, украшение вида ресайклера и еще несколько операций и т.д.).

Создал AbstrctFragment, который расширяет фрагмент и отменяет onCreate onAttach onDestroy и т.д. Поскольку потребляется только тип recyclerview данных, и адаптеры для пересылки данных в recycelrview будут меняться для каждого фрагмента, создайте абстрактную функцию для getAdapter и templatize данных. Каждый из трех фрагментов будет получен из этого AbstractFragment.

    public abstract class AbstractFragment<E> extends Fragment {

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.template_list_fragment, container, false);


            mRecyclerView = (RecyclerView) view.findViewById(R.id.list);

            // get adapter to show item list
            // and fetch data.
            mRecyclerAdapter = getAdapter();
            mRecyclerView.setAdapter(mRecyclerAdapter);

            // show it as vertical list
            mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
            // add seperator between list items.
            mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));

return view;
    }

... onAttach onDetach и любые общие членские функции и переменные-члены, которые поступают для каждого фрагмента.

Теперь файлы макета RecyclerView. Поскольку все они различны в макете, очевидно, они должны быть разными.

RecyclerViewAdapters. Опять же здесь общий код будет состоять из объявлений-членов, CreateViewHolder (здесь только изменения имени макета оставят все код одинаковым) и любую другую функцию, которую разделили бы все эти адаптеры. (что-то вроде фильтрующих элементов списка).

аналогично тому, как мы это делали для фрагментов, вы можете сохранить это в AbstractRecyclerViewAdapter и сделать bindViewholder и т.д. абстрактными функциями и иметь 3 разных recyclerAdapters, которые будут получены из этого AbstractRecyclerViewAdapter..

Ответ 3

слишком поздно, но может быть полезно для кого-то нуждающегося разработчика Ваш адаптер должен выглядеть так, также вы можете добавить верхний и нижний колонтитулы, используя этот образец

public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> {

// Declaring Variable to Understand which View is being worked on
// IF the view under inflation and population is header or Item
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int TYPE_FOOTER = 2;

private Activity mContext;
private ArrayList<DataModel> _mItems;
private int mLayout;
private String mProductHeadingTitle="Heading";
private String mProductHeadingSubTitle="SubHeading";
private String loadingText="LOADING";
private int visibility= View.VISIBLE;

public interface SampleAdapterInterface {
     void itemClicked(int position);
}

SampleAdapterInterface mCallBack;

public SampleAdapter(Activity context, ArrayList<DataModel> items, int item_layout) {
    if (_mItems != null) {
        _mItems.clear();
    }
    this.mContext = context;
    this._mItems = items;
    this.mLayout = item_layout;
    mCallBack = (SampleAdapterInterface) context;
}

@Override
public int getItemCount() {
    return _mItems.size()+2; // +2 for header and footer
}

@Override
public int getItemViewType(int position) {

    if (position==0)
        return TYPE_HEADER;
    else if(position==(_mItems.size()+1))
        return TYPE_FOOTER;

    return TYPE_ITEM;
}

public void setHeaderData(String title,String subTitle)
{
    this.mProductHeadingTitle=title;
    this.mProductHeadingSubTitle=subTitle;

    Log.d("LOG", "ProductHeadingTitle: " + mProductHeadingTitle);
    Log.d("LOG", "ProductHeadingSubTitle: " + mProductHeadingSubTitle);

    notifyDataSetChanged();
}

public void setFooterData(String loadingText,int visibility)
{
    this.loadingText=loadingText;
    this.visibility=visibility;

    Log.d("LOG", "LoadingText: " + loadingText);
    Log.d("LOG", "Visibility: " + visibility);

    notifyDataSetChanged();
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {


    if (viewType == TYPE_HEADER)
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_layout,parent,false);

        ViewHolder vhHeader = new ViewHolder(view,viewType);

        return vhHeader;
    }
    else if (viewType == TYPE_ITEM)
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(mLayout,parent,false);

        //Creating ViewHolder and passing the object of type view
        ViewHolder vhItem = new ViewHolder(view,viewType);

        return vhItem;
    }
    else if (viewType == TYPE_FOOTER)
    {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.footer_lyout,parent,false);

        ViewHolder vhFooter = new ViewHolder(view,viewType);

        return vhFooter;
    }
    return null;
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, int pos) {


    if(viewHolder.Holderid ==0)
    {
        // header view
        Log.d("LOG", "in header binder");
        viewHolder.mProductCatalogTitle.setText(mProductHeadingTitle);
        viewHolder.mProductCatalogSubTitle.setText(mProductHeadingSubTitle);
    }
    else if(viewHolder.Holderid==1)
    {
        final int position=pos-1; // -1 to substract header number
        // your code
    }
    else if(viewHolder.Holderid==2)
    {
        // footer
        Log.d("LOG", "in footer binder");
        viewHolder.mProgressBar.setVisibility(visibility);
        viewHolder.mLoading.setText(loadingText);
    }


}

class ViewHolder extends RecyclerView.ViewHolder {

    int Holderid;

    // header
    TextView mProductCatalogTitle;
    TextView mProductCatalogSubTitle;

    //list
    // item type variable declaration

    // footer
    ProgressBar mProgressBar;
    TextView mLoading;

    public ViewHolder(View itemView, int viewType) {
        super(itemView);
        // Here we set the appropriate view in accordance with the the view type as passed when the holder object is created
        if(viewType == TYPE_HEADER)
        {
            Holderid = 0;
            mProductCatalogTitle = (TextView) itemView.findViewById(R.id.tv_title);
            mProductCatalogSubTitle = (TextView) itemView.findViewById(R.id.tv_subtitle);
        }
        else if(viewType == TYPE_ITEM)
        {
            Holderid = 1;
            itemView.setClickable(true);
            itemView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mCallBack.itemClicked(getAdapterPosition()-1);
                }
            });
            // initialize the view holder
        }

        else if(viewType == TYPE_FOOTER)
        {
            Holderid = 2;
            mLoading = (TextView) itemView.findViewById(R.id.tv_loading);
            mProgressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar);
        }
    }
}

в вашей деятельности

// Adding to adapter as gridview when grid button clicked
private void setProductGridAdapter() {
    mListViewTab.setVisibility(View.VISIBLE);
    mGridViewTab.setVisibility(View.INVISIBLE);

    mSampleAdapter = new SampleAdapter(YourActicity.this,
            yourlist,R.layout.item_product_grid);
    mRecyclerView.setAdapter(mSampleAdapter);

    mRecyclerView.setHasFixedSize(true);

    final GridLayoutManager layoutManager = new GridLayoutManager(getActivity(),
            2,                              //number of columns
            LinearLayoutManager.VERTICAL,   // orientation
            false);                         //reverse layout
    mRecyclerView.setLayoutManager(layoutManager);

    layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
        @Override
        public int getSpanSize(int position) {

            // to show header and footer in full row
            if(position==0 || position==(yourlist.size()+1))
                return layoutManager.getSpanCount();
            else
                return 1;
        }
    });

    mRecyclerView.scrollToPosition(AppController.currentPosition);

    mSampleAdapter.notifyDataSetChanged();

    // Scroll listener for RecyclerView to call load more products
    mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

            int visibleItemCount = layoutManager.getChildCount();
            int totalItemCount = layoutManager.getItemCount();
            int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();

            int lastInScreen = firstVisibleItem + visibleItemCount;
            if ((lastInScreen >= totalItemCount) && !isLoading) {
                //last item
                // do something after last item like load more code or 
                // show No more items string
                mSampleAdapter.setFooterData("NO More Items",View.INVISIBLE);
            }
            AppController.currentPosition = firstVisibleItem;
        }
    });

}

// Adding to adapter as listview when list button clicked
private void setProductListAdapter() {
    mListViewTab.setVisibility(View.INVISIBLE);
    mGridViewTab.setVisibility(View.VISIBLE);

    mSampleAdapter = new SampleAdapter(YourActicity.this, yourlist,R.layout.item_product_list);
    mRecyclerView.setAdapter(mSampleAdapter);

    mRecyclerView.setHasFixedSize(true);

    final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(),
            LinearLayoutManager.VERTICAL,   // orientation
            false);                         //reverse layout
    mRecyclerView.setLayoutManager(layoutManager);

    mRecyclerView.scrollToPosition(AppController.currentPosition);
    mSampleAdapter.notifyDataSetChanged();

    // Scroll listener for RecyclerView to call load more products
    mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

            int visibleItemCount = layoutManager.getChildCount();
            int totalItemCount = layoutManager.getItemCount();
            int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();

            int lastInScreen = firstVisibleItem + visibleItemCount;
            if ((lastInScreen >= totalItemCount) && !isLoading) {
                //last item
                // do something after last item like load more code or 
                // show No more items string
                mSampleAdapter.setFooterData("NO MOre Items",View.INVISIBLE);
            }
            AppController.currentPosition = firstVisibleItem;
        }
    });
}

и ваша активность должна реализовать SampleAdapterInterface в действии для получения обратного вызова от адаптера. Вызовите эти методы при переключении кнопки из сетки в список и наоборот

Ответ 4

//To setViewType maybe is a solution for you.Sample below:  
private static final int TYPE_DESC = 0;
private static final int TYPE_IMAGE = TYPE_DESC + 1;
private static final int TYPE_THREE_TEXT = TYPE_IMAGE + 1;
public int getItemViewType(int position) {
    int type = super.getItemViewType(position);
    try
    {
        type = Integer.parseInt(data.get(position).get("type"));
    } catch (Exception e)
    {
        e.printStackTrace();
    }
    return type;
}

public int getViewTypeCount() {
    return 3;
}
public View getView(int position, View convertView, ViewGroup parent) {
    int type = TYPE_DESC;
    try
    {
        type = Integer.parseInt(data.get(position).get("type"));
    } catch (Exception e)
    {
        e.printStackTrace();
    }
    ViewHolder holder = null;
    if (convertView == null)
    {
        System.out.println("getView::convertView is null");
        holder = new ViewHolder();
        switch (type)
        {
            case TYPE_DESC:
                convertView = View.inflate(getBaseContext(),
                        R.layout.listitem_1, null);
                break;
            case TYPE_IMAGE:
                convertView = View.inflate(getBaseContext(),
                        R.layout.listitem_2, null);
                break;
            case TYPE_THREE_TEXT:
                convertView = View.inflate(getBaseContext(),
                        R.layout.listitem_3, null);
                break;
        }
        convertView.setTag(holder);
    }
    else
    {
        holder = (ViewHolder) convertView.getTag();
    }
    //TODO
    return convertView;
}