Есть ли эквивалент addHeaderView для RecyclerView?
Я ищу эквивалент addHeaderView для просмотра ресайклеров. В основном я хочу, чтобы изображение с двумя кнопками было добавлено в виде заголовка в список. Есть ли другой способ добавить представление заголовка в вид ресайклера? Примером для руководства было бы полезно
EDIT 2 (добавленные макеты фрагментов):
После добавления операторов журналов кажется, что getViewType только когда-либо получает позицию 0. Это приводит к тому, что onCreateView загружает только один макет:
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> onCreateViewHolder, viewtype: 0
10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> onBindViewHolder, viewType: 0
Переход фрагмента для загрузки CommentFragment:
@Override
public void onPhotoFeedItemClick(View view, int position) {
if (fragmentManager == null)
fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (view.getId() == R.id.button_comment){
CommentFragment commentFragment = CommentFragment.newInstance("","", position);
fragmentTransaction.add(R.id.main_activity, commentFragment,"comment_fragment_tag");
fragmentTransaction.addToBackStack(Constants.TAG_COMMENTS);
fragmentTransaction.commit();
}
}
Фрагмент onCreateView:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_comment, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.list_recylclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(_context));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new CommentAdapter(R.layout.row_list_comments, R.layout.row_header_comments, _context, comments);
mRecyclerView.setAdapter(mAdapter);
return view;
}
Фрагмент, содержащий recycleview:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context="co.testapp.fragments.CommentFragment"
android:background="@color/white">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_recylclerview"
android:layout_width="match_parent"
android:layout_height="200dp" />
</RelativeLayout>
</RelativeLayout>
Макет строки комментариев:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_margin="10dp"
android:background="@color/white">
<!--Profile Picture-->
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:id="@+id/profile_picture"
android:background="@color/blue_testapp"/>
<!--Name-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="First Name Last Name"
android:textSize="16dp"
android:textColor="@color/blue_testapp"
android:id="@+id/name_of_poster"
android:layout_toRightOf="@id/profile_picture"
/>
<!--Comment-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginTop="-5dp"
android:text="This is a test comment"
android:textSize="14dp"
android:textColor="@color/black"
android:id="@+id/comment"
android:layout_below="@id/name_of_poster"
android:layout_toRightOf="@id/profile_picture"/>
</RelativeLayout>
Заголовок
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="300dp"
android:id="@+id/header_photo"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
Код адаптера (спасибо его за то, что он начал меня):
public class CommentAdapter extends RecyclerView.Adapter<ViewHolder>{
private final int rowCardLayout;
public static Context mContext;
private final int headerLayout;
private final String [] comments;
private static final int HEADER = 0;
private static final int OTHER = 0;
public CommentAdapter(int rowCardLayout, int headerLayout, Context context, String [] comments) {
this.rowCardLayout = rowCardLayout;
this.mContext = context;
this.comments = comments;
this.headerLayout = headerLayout;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
logger.i("onCreateViewHolder, viewtype: " + i); //viewtype always returns 0 so OTHER layout is never inflated
if (i == HEADER) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(headerLayout, viewGroup, false);
return new ViewHolderHeader(v);
}
else if (i == OTHER){
View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowCardLayout, viewGroup, false);
return new ViewHolderComments(v);
}
else
throw new RuntimeException("Could not inflate layout");
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
logger.i("onBindViewHolder, viewType: " + i);
if (viewHolder instanceof ViewHolderComments)
((ViewHolderComments) viewHolder).comment.setText(comments[i].toString());
if (viewHolder instanceof ViewHolderHeader)
((ViewHolderHeader) viewHolder).header.setImageResource(R.drawable.image2);
else {
logger.e("no instance of viewholder found");
}
}
@Override
public int getItemCount() {
int count = comments.length + 1;
logger.i("getItemCount: " + count);
return count;
}
@Override
public int getItemViewType(int position) {
logger.i("getItemViewType position: " + position);
if (position == HEADER)
return HEADER;
else
return OTHER;
}
public static class ViewHolderComments extends RecyclerView.ViewHolder {
public TextView comment;
public ImageView image;
public ViewHolderComments(View itemView) {
super(itemView);
comment = (TextView) itemView.findViewById(R.id.comment);
image = (ImageView) itemView.findViewById(R.id.image);
}
}
public static class ViewHolderHeader extends RecyclerView.ViewHolder {
public final ImageView header;
public ViewHolderHeader(View itemView){
super(itemView);
header = (ImageView) itemView.findViewById(R.id.header_photo);
}
}
}
Используя приведенный выше код, отображается только макет заголовка, так как viewType всегда равен 0. Он выглядит как this. Если я заставляю другой макет, он выглядит как this:
Ответы
Ответ 1
Существует не простой способ, как listview.addHeaderView()
но вы можете добиться этого, добавив тип в свой адаптер для заголовка.
Вот пример
public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
String[] data;
public HeaderAdapter(String[] data) {
this.data = data;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
//inflate your layout and pass it to view holder
return new VHItem(null);
} else if (viewType == TYPE_HEADER) {
//inflate your layout and pass it to view holder
return new VHHeader(null);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHItem) {
String dataItem = getItem(position);
//cast holder to VHItem and set data
} else if (holder instanceof VHHeader) {
//cast holder to VHHeader and set data for header.
}
}
@Override
public int getItemCount() {
return data.length + 1;
}
@Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
private String getItem(int position) {
return data[position - 1];
}
class VHItem extends RecyclerView.ViewHolder {
TextView title;
public VHItem(View itemView) {
super(itemView);
}
}
class VHHeader extends RecyclerView.ViewHolder {
Button button;
public VHHeader(View itemView) {
super(itemView);
}
}
}
ссылка на gist → здесь
Ответ 2
Простое и многоразовое ItemDecoration
Статические заголовки могут быть легко добавлены с помощью ItemDecoration
и без каких-либо дальнейших изменений.
// add the decoration. done.
HeaderDecoration headerDecoration = new HeaderDecoration(/* init */);
recyclerView.addItemDecoration(headerDecoration);
Украшение также можно использовать повторно, так как нет необходимости изменять адаптер или RecyclerView
вообще.
Пример кода, приведенного ниже, потребует просмотра, чтобы добавить в верхнюю часть, которую можно просто раздуть, как и все остальное. Это может выглядеть так:
Почему static?
Если вам просто нужно отображать текст и изображения, это решение для вас - нет возможности для взаимодействия с пользователем, как кнопки или просмотр пейджеров, поскольку он будет просто нарисован поверх вашего списка.
Обработка пустых списков
Если нет украшения для украшения, украшение не будет нарисовано. Вам все равно придется обрабатывать пустой список. (Одним из возможных способов решения проблемы было бы добавление фиктивного элемента в адаптер.)
Код
Вы можете найти полный исходный код здесь, на GitHub, включая Builder
, чтобы помочь с инициализацией декоратора, или просто используйте приведенный ниже код и поставьте собственные значения конструктору.
Не забудьте установить правильный layout_height
для вашего вида. например match_parent
может работать неправильно.
public class HeaderDecoration extends RecyclerView.ItemDecoration {
private final View mView;
private final boolean mHorizontal;
private final float mParallax;
private final float mShadowSize;
private final int mColumns;
private final Paint mShadowPaint;
public HeaderDecoration(View view, boolean scrollsHorizontally, float parallax, float shadowSize, int columns) {
mView = view;
mHorizontal = scrollsHorizontally;
mParallax = parallax;
mShadowSize = shadowSize;
mColumns = columns;
if (mShadowSize > 0) {
mShadowPaint = new Paint();
mShadowPaint.setShader(mHorizontal ?
new LinearGradient(mShadowSize, 0, 0, 0,
new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
new float[]{0f, .5f, 1f},
Shader.TileMode.CLAMP) :
new LinearGradient(0, mShadowSize, 0, 0,
new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)},
new float[]{0f, .5f, 1f},
Shader.TileMode.CLAMP));
} else {
mShadowPaint = null;
}
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// layout basically just gets drawn on the reserved space on top of the first view
mView.layout(parent.getLeft(), 0, parent.getRight(), mView.getMeasuredHeight());
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (parent.getChildAdapterPosition(view) == 0) {
c.save();
if (mHorizontal) {
c.clipRect(parent.getLeft(), parent.getTop(), view.getLeft(), parent.getBottom());
final int width = mView.getMeasuredWidth();
final float left = (view.getLeft() - width) * mParallax;
c.translate(left, 0);
mView.draw(c);
if (mShadowSize > 0) {
c.translate(view.getLeft() - left - mShadowSize, 0);
c.drawRect(parent.getLeft(), parent.getTop(), mShadowSize, parent.getBottom(), mShadowPaint);
}
} else {
c.clipRect(parent.getLeft(), parent.getTop(), parent.getRight(), view.getTop());
final int height = mView.getMeasuredHeight();
final float top = (view.getTop() - height) * mParallax;
c.translate(0, top);
mView.draw(c);
if (mShadowSize > 0) {
c.translate(0, view.getTop() - top - mShadowSize);
c.drawRect(parent.getLeft(), parent.getTop(), parent.getRight(), mShadowSize, mShadowPaint);
}
}
c.restore();
break;
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) < mColumns) {
if (mHorizontal) {
if (mView.getMeasuredWidth() <= 0) {
mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
}
outRect.set(mView.getMeasuredWidth(), 0, 0, 0);
} else {
if (mView.getMeasuredHeight() <= 0) {
mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
}
outRect.set(0, mView.getMeasuredHeight(), 0, 0);
}
} else {
outRect.setEmpty();
}
}
}
Обратите внимание: проект GitHub - это моя личная игровая площадка. Это не проверено полностью, поэтому библиотеки еще нет.
Что он делает?
An ItemDecoration
- дополнительный чертеж для элемента списка. В этом случае украшение рисуется в верхней части первого элемента.
Вид измеряется и выкладывается, затем он обращается к вершине первого элемента. Если добавляется эффект параллакса, он также будет привязан к правильным границам.
Ответ 3
Не стесняйтесь использовать мою библиотеку, доступную здесь.
Он позволяет создавать заголовок View
для любого RecyclerView
, который использует LinearLayoutManager
или GridLayoutManager
только с помощью простого вызова метода.
Ответ 4
Собираюсь показать вам сделать заголовок с элементами в представлении Recycler.
Шаг 1- Добавьте зависимость в ваш файл Gradle.
compile 'com.android.support:recyclerview-v7:23.2.0'
// CardView
compile 'com.android.support:cardview-v7:23.2.0'
Cardview используется для украшения.
Step2- Сделайте три XML файла. Один для основного действия. Второй для расположения заголовка. Третий для расположения элемента списка.
activity_main.xml
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
header.xml
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="2dp">
<TextView
android:id="@+id/txtHeader"
android:gravity="center"
android:textColor="#000000"
android:textSize="@dimen/abc_text_size_large_material"
android:background="#DCDCDC"
android:layout_width="match_parent"
android:layout_height="50dp" />
</android.support.v7.widget.CardView>
list.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="1dp">
<TextView
android:id="@+id/txtName"
android:text="abc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>
</LinearLayout>
Шаг 3- Создайте три класса бобов.
Header.java
public class Header extends ListItem {
private String header;
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
ContentItem.java
public class ContentItem extends ListItem {
private String name;
private String rollnumber;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
public String getRollnumber() {
return rollnumber;
}
public void setRollnumber(String rollnumber) {
this.rollnumber = rollnumber;
}
}
ListItem.java
public class ListItem {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Шаг 4- Создайте адаптер с именем MyRecyclerAdapter.java
public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
//Header header;
List<ListItem> list;
public MyRecyclerAdapter(List<ListItem> headerItems) {
this.list = headerItems;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == TYPE_HEADER) {
View v = inflater.inflate(R.layout.header, parent, false);
return new VHHeader(v);
} else {
View v = inflater.inflate(R.layout.list, parent, false);
return new VHItem(v);
}
throw new IllegalArgumentException();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHHeader) {
// VHHeader VHheader = (VHHeader)holder;
Header currentItem = (Header) list.get(position);
VHHeader VHheader = (VHHeader)holder;
VHheader.txtTitle.setText(currentItem.getHeader());
} else if (holder instanceof VHItem)
ContentItem currentItem = (ContentItem) list.get(position);
VHItem VHitem = (VHItem)holder;
VHitem.txtName.setText(currentItem.getName());
}
}
@Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return list.get(position) instanceof Header;
}
@Override
public int getItemCount() {
return list.size();
}
class VHHeader extends RecyclerView.ViewHolder{
TextView txtTitle;
public VHHeader(View itemView) {
super(itemView);
this.txtTitle = (TextView) itemView.findViewById(R.id.txtHeader);
}
}
class VHItem extends RecyclerView.ViewHolder{
TextView txtName;
public VHItem(View itemView) {
super(itemView);
this.txtName = (TextView) itemView.findViewById(R.id.txtName);
}
}
}
Шаг 5- В MainActivity добавьте следующий код:
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
List<List<ListItem>> arraylist;
MyRecyclerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
adapter = new MyRecyclerAdapter(getList());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
private ArrayList<ListItem> getList() {
ArrayList<ListItem> arrayList = new ArrayList<>();
for(int j = 0; j <= 4; j++) {
Header header = new Header();
header.setHeader("header"+j);
arrayList.add(header);
for (int i = 0; i <= 3; i++) {
ContentItem item = new ContentItem();
item.setRollnumber(i + "");
item.setName("A" + i);
arrayList.add(item);
}
}
return arrayList;
}
}
Функция getList() динамически генерирует данные для заголовков и элементов списка.
Ответ 5
Вы можете достичь этого, используя библиотеку SectionedRecyclerViewAdapter, в ней есть понятие "Разделы", где у какого Раздела есть Верхний колонтитул, Нижний колонтитул и Содержимое (список элементов). В вашем случае вам может понадобиться только один раздел, но вы можете иметь много:
1) Создайте собственный класс раздела:
class MySection extends StatelessSection {
List<String> myList = Arrays.asList(new String[] {"Item1", "Item2", "Item3" });
public MySection() {
// call constructor with layout resources for this Section header, footer and items
super(R.layout.section_header, R.layout.section_footer, R.layout.section_item);
}
@Override
public int getContentItemsTotal() {
return myList.size(); // number of items of this section
}
@Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
@Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(myList.get(position));
}
}
2) Создайте пользовательский ViewHolder для элементов:
class MyItemViewHolder extends RecyclerView.ViewHolder {
private final TextView tvItem;
public MyItemViewHolder(View itemView) {
super(itemView);
tvItem = (TextView) itemView.findViewById(R.id.tvItem);
}
}
3) Настройте ваш ReclyclerView с помощью SectionedRecyclerViewAdapter
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
MySection mySection = new MySection();
// Add your Sections
sectionAdapter.addSection(mySection);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
Ответ 6
Вы можете просто разместить свой заголовок и свой RecyclerView в NestedScrollView:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<include
layout="@layout/your_header"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/list_recylclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Чтобы прокрутка работала правильно, вам нужно отключить вложенную прокрутку на вашем RecyclerView:
myRecyclerView.setNestedScrollingEnabled(false);
Ответ 7
Основываясь на этом сообщении, я создал подкласс RecyclerView.Adapter, который поддерживает произвольное количество верхних и нижних колонтитулов.
https://gist.github.com/mheras/0908873267def75dc746
Хотя это, кажется, решение, я также считаю, что эта вещь должна управляться LayoutManager. К сожалению, мне это нужно сейчас, и у меня нет времени для реализации StaggeredGridLayoutManager с нуля (и даже не от него).
Я все еще тестирую его, но вы можете попробовать его, если хотите. Пожалуйста, дайте мне знать, если вы найдете какие-либо проблемы с ним.
Ответ 8
У Native API нет такой функции "addHeader", но есть концепция "addItem".
Я смог включить эту специфическую особенность заголовков и расшириться для нижних колонтитулов, а также в FlexibleAdapter проект. Я назвал его прокручиваемыми верхними и нижними колонтитулами.
Вот как они работают:
Прокручиваемые верхние и нижние колонтитулы - это специальные элементы, которые прокручиваются вместе со всеми остальными, но они не относятся к основным элементам (бизнес-элементам), и они всегда обрабатываются адаптером рядом с основными элементами. Эти предметы постоянно расположены в первой и последней позициях.
Там можно много рассказать о них, лучше прочитать подробную страницу wiki.
Кроме того, FlexibleAdapter позволяет создавать заголовки/разделы, а также вы можете иметь их липкие и десятки других функций, таких как расширяемые элементы, бесконечные прокрутки, расширения пользовательского интерфейса и т.д.... все в одной библиотеке!
Ответ 9
Существует еще одно решение, которое охватывает все варианты использования выше: CompoundAdapter:
https://github.com/negusoft/CompoundAdapter-android
Вы можете создать AdapterGroup, в которой есть ваш адаптер, как есть, вместе с адаптером с одним элементом для представления заголовка. Код прост и понятен:
AdapterGroup adapterGroup = new AdapterGroup();
adapterGroup.addAdapter(SingleAdapter.create(R.layout.header));
adapterGroup.addAdapter(new CommentAdapter(...));
recyclerView.setAdapter(adapterGroup);
AdapterGroup допускает также вложенность, поэтому для адаптера с разделами вы можете создать группу AdapterGroup для каждого раздела. Затем поместите все разделы в корневую группу AdapterGroup.
Ответ 10
HeaderView зависит от LayoutManager. Ни один из разработчиков LayoutManagers по умолчанию не поддерживает это и, вероятно, не будет. HeaderView в ListView создает большую сложность без каких-либо существенных преимуществ.
Я бы предложил создать класс базового адаптера, который добавляет элементы для заголовков, если они предусмотрены. Не забудьте переопределить методы уведомления, чтобы их правильно компенсировать в зависимости от наличия заголовка или нет.
Ответ 11
First - extends RecyclerView.Adapter<RecyclerView.ViewHolder>
public class MenuAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
После - переопределить метод getItemViewTpe
*** Более важно
@Override
public int getItemViewType(int position) {
return position;
}
метод onCreateViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
View header = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_header_item, parent, false);
Log.d("onCreateViewHolder", String.valueOf(viewType));
if (viewType == 0) {
return new MenuLeftHeaderViewHolder(header, onClickListener);
} else {
return new MenuLeftViewHolder(view, onClickListener);
}
}
метод onBindViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (position == 0) {
MenuHeaderViewHolder menuHeaderViewHolder = (MenuHeaderViewHolder) holder;
menuHeaderViewHolder.mTitle.setText(sMenuTitles[position]);
menuHeaderViewHolder.mImage.setImageResource(sMenuImages[position]);
} else {
MenuViewHolder menuLeftViewHolder = (MenuLeftViewHolder) holder;
menuViewHolder.mTitle.setText(sMenuTitles[position]);
menuViewHolder.mImage.setImageResource(sMenuImages[position]);
}
}
в финише реализуется класс ViewHolders static
public static class MenuViewHolder extends RecyclerView.ViewHolder
public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder
Ответ 12
здесь некоторые предметы для recyclerview
public class HeaderItemDecoration extends RecyclerView.ItemDecoration {
private View customView;
public HeaderItemDecoration(View view) {
this.customView = view;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
customView.layout(parent.getLeft(), 0, parent.getRight(), customView.getMeasuredHeight());
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (parent.getChildAdapterPosition(view) == 0) {
c.save();
final int height = customView.getMeasuredHeight();
final int top = view.getTop() - height;
c.translate(0, top);
customView.draw(c);
c.restore();
break;
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) == 0) {
customView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
outRect.set(0, customView.getMeasuredHeight(), 0, 0);
} else {
outRect.setEmpty();
}
}
}
Ответ 13
Я сделал реализацию на основе @hister's для моих личных целей, но использовал наследование.
Я скрываю механизмы детали реализации (например, добавляет 1 к itemCount
, itemCount
1 из position
) в абстрактном суперклассическом классе HeadingableRecycleAdapter
, реализуя необходимые методы из Adapter, например onBindViewHolder
, getItemViewType
и getItemCount
, делая эти методы окончательными и предоставляя новые методы со скрытой логикой клиенту:
-
onAddViewHolder(RecyclerView.ViewHolder holder, int position)
, -
onCreateViewHolder(ViewGroup parent)
, -
itemCount()
Вот класс HeadingableRecycleAdapter
и клиент. Я оставил макет заголовка немного жестко закодированным, потому что он соответствует моим потребностям.
public abstract class HeadingableRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int HEADER_VIEW_TYPE = 0;
@LayoutRes
private int headerLayoutResource;
private String headerTitle;
private Context context;
public HeadingableRecycleAdapter(@LayoutRes int headerLayoutResourceId, String headerTitle, Context context) {
this.headerLayoutResource = headerLayoutResourceId;
this.headerTitle = headerTitle;
this.context = context;
}
public Context context() {
return context;
}
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == HEADER_VIEW_TYPE) {
return new HeaderViewHolder(LayoutInflater.from(context).inflate(headerLayoutResource, parent, false));
}
return onCreateViewHolder(parent);
}
@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
if (viewType == HEADER_VIEW_TYPE) {
HeaderViewHolder vh = (HeaderViewHolder) holder;
vh.bind(headerTitle);
} else {
onAddViewHolder(holder, position - 1);
}
}
@Override
public final int getItemViewType(int position) {
return position == 0 ? 0 : 1;
}
@Override
public final int getItemCount() {
return itemCount() + 1;
}
public abstract int itemCount();
public abstract RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
public abstract void onAddViewHolder(RecyclerView.ViewHolder holder, int position);
}
@PerActivity
public class IngredientsAdapter extends HeadingableRecycleAdapter {
public static final String TITLE = "Ingredients";
private List<Ingredient> itemList;
@Inject
public IngredientsAdapter(Context context) {
super(R.layout.layout_generic_recyclerview_cardified_header, TITLE, context);
}
public void setItemList(List<Ingredient> itemList) {
this.itemList = itemList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ViewHolder(LayoutInflater.from(context()).inflate(R.layout.item_ingredient, parent, false));
}
@Override
public void onAddViewHolder(RecyclerView.ViewHolder holder, int position) {
ViewHolder vh = (ViewHolder) holder;
vh.bind(itemList.get(position));
}
@Override
public int itemCount() {
return itemList == null ? 0 : itemList.size();
}
private String getQuantityFormated(double quantity, String measure) {
if (quantity == (long) quantity) {
return String.format(Locale.US, "%s %s", String.valueOf(quantity), measure);
} else {
return String.format(Locale.US, "%.1f %s", quantity, measure);
}
}
class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.text_ingredient)
TextView txtIngredient;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
void bind(Ingredient ingredient) {
String ingredientText = ingredient.getIngredient();
txtIngredient.setText(String.format(Locale.US, "%s %s ", getQuantityFormated(ingredient.getQuantity(),
ingredient.getMeasure()), Character.toUpperCase(ingredientText.charAt(0)) +
ingredientText
.substring(1)));
}
}
}
Ответ 14
Может быть, оберните заголовок и recyclerview в coordinatorlayout:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<View
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
Ответ 15
Возможно http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ поможет. Он использует только RecyclerView и CardView.
Вот адаптер:
public class DifferentRowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<CityEvent> mList;
public DifferentRowAdapter(List<CityEvent> list) {
this.mList = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case CITY_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_city, parent, false);
return new CityViewHolder(view);
case EVENT_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_event, parent, false);
return new EventViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
CityEvent object = mList.get(position);
if (object != null) {
switch (object.getType()) {
case CITY_TYPE:
((CityViewHolder) holder).mTitle.setText(object.getName());
break;
case EVENT_TYPE:
((EventViewHolder) holder).mTitle.setText(object.getName());
((EventViewHolder) holder).mDescription.setText(object.getDescription());
break;
}
}
}
@Override
public int getItemCount() {
if (mList == null)
return 0;
return mList.size();
}
@Override
public int getItemViewType(int position) {
if (mList != null) {
CityEvent object = mList.get(position);
if (object != null) {
return object.getType();
}
}
return 0;
}
public static class CityViewHolder extends RecyclerView.ViewHolder {
private TextView mTitle;
public CityViewHolder(View itemView) {
super(itemView);
mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
}
}
public static class EventViewHolder extends RecyclerView.ViewHolder {
private TextView mTitle;
private TextView mDescription;
public EventViewHolder(View itemView) {
super(itemView);
mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
mDescription = (TextView) itemView.findViewById(R.id.descriptionTextView);
}
}
}
И вот объект:
public class CityEvent {
public static final int CITY_TYPE = 0;
public static final int EVENT_TYPE = 1;
private String mName;
private String mDescription;
private int mType;
public CityEvent(String name, String description, int type) {
this.mName = name;
this.mDescription = description;
this.mType = type;
}
public String getName() {
return mName;
}
public void setName(String name) {
this.mName = name;
}
public String getDescription() {
return mDescription;
}
public void setDescription(String description) {
this.mDescription = description;
}
public int getType() {
return mType;
}
public void setType(int type) {
this.mType = type;
}
}
Ответ 16
вы можете создать addHeaderView и использовать
adapter.addHeaderView(View)
.
Этот код создает addHeaderView
для более одного заголовка. заголовки должны иметь:
android:layout_height="wrap_content"
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_ITEM = -1;
public class MyViewSHolder extends RecyclerView.ViewHolder {
public MyViewSHolder (View view) {
super(view);
}
// put you code. for example:
View mView;
...
}
public class ViewHeader extends RecyclerView.ViewHolder {
public ViewHeader(View view) {
super(view);
}
}
private List<View> mHeaderViews = new ArrayList<>();
public void addHeaderView(View headerView) {
mHeaderViews.add(headerView);
}
@Override
public int getItemCount() {
return ... + mHeaderViews.size();
}
@Override
public int getItemViewType(int position) {
if (mHeaderViews.size() > position) {
return position;
}
return TYPE_ITEM;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType != TYPE_ITEM) {
//inflate your layout and pass it to view holder
return new ViewHeader(mHeaderViews.get(viewType));
}
...
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int basePosition1) {
if (holder instanceof ViewHeader) {
return;
}
int basePosition = basePosition1 - mHeaderViews.size();
...
}
}
Ответ 17
Прошло несколько лет, но на всякий случай кто-нибудь прочтет это позже...
Используя приведенный выше код, отображается только макет заголовка, поскольку viewType всегда равен 0.
Проблема в константном объявлении:
private static final int HEADER = 0;
private static final int OTHER = 0; <== bug
Если вы объявите их обоих как ноль, то вы всегда получите ноль!