リサイクラービュー用にaddHeaderViewと同等のものを探しています。基本的には2つのボタンを持つ画像をリストビューのヘッダーとして追加したいと思います。リサイクルビューにヘッダビューを追加する別の方法はありますか?ガイダンスの例は役に立つでしょう
編集2(追加されたフラグメントレイアウト):
ログステートメントを追加した後は、getViewTypeが0の位置しか受け取らないように見えます。これにより、onCreateViewは1つのレイアウトのみをロードします。
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 のようになります。
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);
}
}
}
ItemDecoration
静的 ヘッダーはItemDecoration
と を使って簡単に追加できます。
// add the decoration. done.
HeaderDecoration headerDecoration = new HeaderDecoration(/* init */);
recyclerView.addItemDecoration(headerDecoration);
アダプタやRecyclerView
を変更する必要が全くないので、装飾はまた再利用可能です。
以下に提供されているサンプルコードはトップに追加するためのビューを必要とします。これは次のようになります。
テキストと画像を表示するだけの場合は、このソリューションが適しています。ボタンやビューページャーなどのユーザー操作はリストの一番上に表示されるため、ユーザーによる操作はできません。
飾る視野がなければ、飾りは描かれません。あなたはまだあなた自身で空のリストを処理しなければならないでしょう。 (回避策の1つとして、アダプターにダミー項目を追加することが考えられます。)
完全なソースコードを見つけることができます ここで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プロジェクトは私の個人的な遊び場です。それは徹底的にテストされていません、そのためライブラリ まだ がないのはこのためです。
ItemDecoration
はリストの項目への追加の描画です。この場合、装飾は最初の項目の上部に描画されます。
ビューが測定されてレイアウトされ、次に最初の項目の一番上に描画されます。視差効果が追加されている場合、それはまた正しい境界にクリップされます。
私のライブラリを自由に使ってください、利用可能です ここ 。
簡単なメソッド呼び出しでView
またはRecyclerView
を使用するすべてのLinearLayoutManager
に対してヘッダーGridLayoutManager
を作成しましょう。
Recyclerビューでアイテムを使ってヘッダーを作成するように表示します。
ステップ1 - gradleファイルに依存関係を追加します。
compile 'com.Android.support:recyclerview-v7:23.2.0'
// CardView
compile 'com.Android.support:cardview-v7:23.2.0'
Cardviewは装飾目的に使用されます。
ステップ2 - 3つの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 - 3つのBeanクラスを作成します。
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()は、ヘッダとリストアイテムのデータを動的に生成しています。
ライブラリ SectionedRecyclerViewAdapter を使ってそれを達成することができます、それはどのセクションがHeader、FooterおよびContent(項目のリスト)を持つかという「セクション」の概念を持ちます。あなたのケースでは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));
}
}
class MyItemViewHolder extends RecyclerView.ViewHolder {
private final TextView tvItem;
public MyItemViewHolder(View itemView) {
super(itemView);
tvItem = (TextView) itemView.findViewById(R.id.tvItem);
}
}
// 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);
ヘッダと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);
このpost に基づいて、私は任意の数のヘッダとフッタをサポートするRecyclerView.Adapterのサブクラスを作成しました。
https://Gist.github.com/mheras/0908873267def75dc746
これは解決策のようですが、これもLayoutManagerで管理する必要があると思います。残念ながら、今はそれが必要で、StaggeredGridLayoutManagerを一から実装する時間もありません(さらに拡張することもできません)。
まだテスト中ですが、試してみることもできます。あなたがそれに関して何か問題を見つけたら私に知らせてください。
ネイティブAPIにはそのような「addHeader」機能はありませんが、「addItem」という概念があります。
私は自分のFlexibleAdapterプロジェクトで、ヘッダのこの特定の機能とフッタのための拡張を含めることができました。 スクロール可能なヘッダーとフッター と呼びました。
ここでそれらがどのように働くか:
スクロール可能なヘッダーとフッターは、他のすべてのアイテムと一緒にスクロールする特別な項目ですが、主要項目(ビジネス項目)には属しておらず、常に主要項目の横にあるアダプターによって処理されます。それらの項目は、最初と最後の位置に永続的に配置されています。
詳細については wikiページ を読むのがいいでしょう。
さらにFlexibleAdapterはヘッダ/セクションを作成することを可能にし、それらをstickyおよび他の何十もの拡張可能なアイテム、エンドレススクロール、UI拡張などの機能を持つことができます。
上記のすべての使用例をカバーするもう1つの解決策があります。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に配置します。
こちらのリサイクル業者の商品の装飾
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();
}
}
}
HeaderViewはLayoutManagerに依存します。デフォルトのLayoutManagerのどれもこれをサポートしていないし、おそらく文句を言わない。 ListViewのHeaderViewを使用すると、大きな利点がなくても複雑になります。
提供されている場合は、ヘッダーの項目を追加する基本アダプタークラスを作成することをお勧めします。ヘッダーが存在するかどうかに応じて、notify *メソッドをオーバーライドして正しくオフセットすることを忘れないでください。
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
私は私の個人的な目的のために @ hister's に基づいて実装を行いましたが、継承を使います。
itemCount
name__、position
name__、HeadingableRecycleAdapter
name__、onBindViewHolder
name__のようなアダプタから必要なメソッドを実装し、そのメソッドを最終的に非表示にし、新しいメソッドを非表示にして、実装の詳細メカニズム(getItemViewType
name__に1を加算、getItemCount
name__から1を減算)を隠しますクライアントへ:
onAddViewHolder(RecyclerView.ViewHolder holder, int position)
、onCreateViewHolder(ViewGroup parent)
、itemCount()
これがHeadingableRecycleAdapter
name__クラスとクライアントです。私はヘッダのレイアウトを少しハードコーディングしたままにしました。
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)));
}
}
}
多分headerとrecyclerviewを コーディネーターレイアウトにラップしてください :
<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" />
おそらく 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;
}
}
あなたはaddHeaderViewを作成して使用することができます
adapter.addHeaderView(View)
。
このコードは1つ以上のヘッダのために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();
...
}
}
数年が経ちましたが、万が一これを後で読んでいる場合に備えて...
上記のコードを使用すると、viewTypeは常に0であるため、ヘッダーレイアウトのみが表示されます。
問題は定数宣言にあります。
private static final int HEADER = 0;
private static final int OTHER = 0; <== bug
両方ともゼロとして宣言すると、常にゼロになります!