RecyclerViewがあり、次の動作が必要です。
この動作を実装する方法を教えてください。
RecyclerView.ItemDecorationを使用して、この動作を実装できます。
public class StickyFooterItemDecoration extends RecyclerView.ItemDecoration {
/**
* Top offset to completely hide footer from the screen and therefore avoid noticeable blink during changing position of the footer.
*/
private static final int OFF_SCREEN_OFFSET = 5000;
@Override
public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent, RecyclerView.State state) {
int adapterItemCount = parent.getAdapter().getItemCount();
if (isFooter(parent, view, adapterItemCount)) {
//For the first time, each view doesn't contain any parameters related to its size,
//hence we can't calculate the appropriate offset.
//In this case, set a big top offset and notify adapter to update footer one more time.
//Also, we shouldn't do it if footer became visible after scrolling.
if (view.getHeight() == 0 && state.didStructureChange()) {
hideFooterAndUpdate(outRect, view, parent);
} else {
outRect.set(0, calculateTopOffset(parent, view, adapterItemCount), 0, 0);
}
}
}
private void hideFooterAndUpdate(Rect outRect, final View footerView, final RecyclerView parent) {
outRect.set(0, OFF_SCREEN_OFFSET, 0, 0);
footerView.post(new Runnable() {
@Override
public void run() {
parent.getAdapter().notifyDataSetChanged();
}
});
}
private int calculateTopOffset(RecyclerView parent, View footerView, int itemCount) {
int topOffset = parent.getHeight() - visibleChildsHeightWithFooter(parent, footerView, itemCount);
return topOffset < 0 ? 0 : topOffset;
}
private int visibleChildsHeightWithFooter(RecyclerView parent, View footerView, int itemCount) {
int totalHeight = 0;
//In the case of dynamic content when adding or removing are possible itemCount from the adapter is reliable,
//but when the screen can fit fewer items than in adapter, getChildCount() from RecyclerView should be used.
int onScreenItemCount = Math.min(parent.getChildCount(), itemCount);
for (int i = 0; i < onScreenItemCount - 1; i++) {
totalHeight += parent.getChildAt(i).getHeight();
}
return totalHeight + footerView.getHeight();
}
private boolean isFooter(RecyclerView parent, View view, int itemCount) {
return parent.getChildAdapterPosition(view) == itemCount - 1;
}
}
RecyclerViewの高さには必ずmatch_parentを設定してください。
サンプルアプリケーションをご覧ください https://github.com/JohnKuper/recyclerview-sticky-footer およびその仕組み http://sendvid.com/nbpj0806
このソリューションの大きな欠点は、(装飾の内部ではなく)アプリケーション全体でnotifyDataSetChanged()の後でのみ正しく機能することです。より具体的な通知では、正しく機能せず、それらをサポートするには、より多くのロジックが必要になります。また、ライブラリrecyclerview-stickyheadersからeowiseによって洞察を得ることができますこのソリューションを改善します。
Dmitriy Korobeynikovの即興演奏と、通知データセットの呼び出しの問題の解決が変更されました
public class StickyFooterItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, final View view, final RecyclerView parent,
RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int adapterItemCount = parent.getAdapter().getItemCount();
if (adapterItemCount == RecyclerView.NO_POSITION || (adapterItemCount - 1) != position) {
return;
}
outRect.top = calculateTopOffset(parent, view, adapterItemCount);
}
private int calculateTopOffset(RecyclerView parent, View footerView, int itemCount) {
int topOffset =
parent.getHeight() - parent.getPaddingTop() - parent.getPaddingBottom()
- visibleChildHeightWithFooter(parent, footerView, itemCount);
return topOffset < 0 ? 0 : topOffset;
}
private int visibleChildHeightWithFooter(RecyclerView parent, View footerView, int itemCount) {
int totalHeight = 0;
int onScreenItemCount = Math.min(parent.getChildCount(), itemCount);
for (int i = 0; i < onScreenItemCount - 1; i++) {
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) parent.getChildAt(i)
.getLayoutParams();
int height =
parent.getChildAt(i).getHeight() + layoutParams.topMargin
+ layoutParams.bottomMargin;
totalHeight += height;
}
int footerHeight = footerView.getHeight();
if (footerHeight == 0) {
fixLayoutSize(footerView, parent);
footerHeight = footerView.getHeight();
}
footerHeight = footerHeight + footerView.getPaddingBottom() + footerView.getPaddingTop();
return totalHeight + footerHeight;
}
private void fixLayoutSize(View view, ViewGroup parent) {
// Check if the view has a layout parameter and if it does not create one for it
if (view.getLayoutParams() == null) {
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
// Create a width and height spec using the parent as an example:
// For width we make sure that the item matches exactly what it measures from the parent.
// IE if layout says to match_parent it will be exactly parent.getWidth()
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
// For the height we are going to create a spec that says it doesn't really care what is calculated,
// even if its larger than the screen
int heightSpec = View.MeasureSpec
.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
// Get the child specs using the parent spec and the padding the parent has
int childWidth = ViewGroup.getChildMeasureSpec(widthSpec,
parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width);
int childHeight = ViewGroup.getChildMeasureSpec(heightSpec,
parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height);
// Finally we measure the sizes with the actual view which does margin and padding changes to the sizes calculated
view.measure(childWidth, childHeight);
// And now we setup the layout for the view to ensure it has the correct sizes.
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
}
ウェイト付きのLinearlayoutを使用しています。フッターの重みに複数の値を作成しましたが、完全に機能します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@color/white"
Android:orientation="vertical"
<include layout="@layout/header" />
<Android.support.v7.widget.RecyclerView
Android:id="@+id/recycleView"
Android:layout_width="match_parent"
Android:layout_height="0dp"
Android:layout_weight="0.5"
tools:layout_height="0dp"
tools:listitem="@layout/row" />
<TextView
Android:layout_width="match_parent"
Android:layout_height="0dp"
Android:layout_weight="@dimen/footer_weight"
Android:padding="@dimen/extra_padding"
Android:paddingEnd="@dimen/small_padding"
Android:paddingLeft="@dimen/small_padding"
Android:paddingRight="@dimen/small_padding"
Android:paddingStart="@dimen/small_padding"
Android:text="@string/contact"
Android:textColor="@color/grey" />
</LinearLayout>
これらのソリューションはすべて機能しません。アプリを最小化して再度開くと、フッターが画面の下部よりも低くなり、アイテムが1〜2個しかない場合でも、スクロールして表示する必要があります。 xmlのリサイクラービューの下にフッタービューを追加できます。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:background="@Android:color/white">
<Android.support.v4.widget.NestedScrollView
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fillViewport="true"
Android:overScrollMode="never"
Android:scrollbars="none">
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="vertical">
<Android.support.v7.widget.RecyclerView
Android:id="@+id/recyclerView"
Android:layout_width="match_parent"
Android:layout_height="wrap_content" />
<Android.support.v4.widget.Space
Android:layout_width="match_parent"
Android:layout_height="0dp"
Android:layout_weight="1"
Android:minHeight="1dp" />
<FrameLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content">
<include layout="@layout/recyclerView_footer" />
</FrameLayout>
</LinearLayout>
</Android.support.v4.widget.NestedScrollView>
注意してください-私はNestedScrollViewを
recyclerView.isNestedScrollingEnabled = false
SpaceView has weight 1 and height = 0dp
とlinear layout
とNestedScrollView has height = match_parent
内のこれらすべてのもので、フッターが下部に固定され、リストが大きくなるとさらに移動します。
class FooterViewHolder(private val parent: ViewGroup) {
...
fun bind(item: Item) {
...
itemView.post(::adjustTop)
}
private fun adjustTop() {
val parent = parent as RecyclerView
var topOffset = parent.height
for (child in parent.children) topOffset -= child.height
(itemView.layoutParams as ViewGroup.MarginLayoutParams)
.setMargins(0, topOffset.coerceAtLeast(0), 0, 0)
}
}
これは古い質問であることは知っていますが、将来そのような決定を探す人のために答えを追加します。アイテムが少ないかまったくない場合に備えて、画面の下部に最後のアイテムを保持することは[〜#〜]可能[〜#〜]です。アイテムが多い場合は、最後のアイテムをrecyclerviewでスクロールします。
達成方法。 RecyclerViewアダプターは、いくつかのビュータイプを適用する必要があります。リストアイテムとして表示されるビュー。ビュー。フッターとして表示する必要があります。空のビュー。さまざまなビューのアイテムをRecyclerViewに配置する方法をここで確認できます: https://stackoverflow.com/a/29362643/6329995 メインリストとフッタービューの間に空のビューを見つけます。次に、空のビューのonBindViewHolderで、メインリストビューとフッタービューがすべての画面を占めるかどうかを確認します。はいの場合-空のビューの高さをゼロに設定します。そうでない場合は、アイテムやフッターに表示されないように見える高さに設定します。それで全部です。行を削除/追加するときに、その高さを動的に更新することもできます。リストを更新した後、空のスペースアイテムに対してnotifyItemChangedを呼び出すだけです。
また、RecyclerViewの高さをwrap_contentではなくmatch_parentまたは正確な高さに設定します。
お役に立てれば。