gpt4 book ai didi

android - 从 RecyclerView.Adapter 中删除项目后,RecyclerView.ItemDecoration 不会更新

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:29:56 25 4
gpt4 key购买 nike

我的问题

我怎样才能得到我的 ItemDecoration当我从我的适配器中删除给定 View 然后调用 notifyItemRemoved() 时,“更新”其他 View 的项目偏移量?

在我的特殊情况下,我有一个 ItemDecoration为每个项目提供少量的顶部偏移量,并为最后一个项目提供大量的底部偏移量。当我删除最后一项时,成为新的最后一项 的 View 没有任何底部偏移。

之前和之后:

enter image description here enter image description here

如果我随后滚动列表,当我返回到列表底部时,我的"new"最后一项具有正确的偏移量。只要“新的”最后一项在屏幕上保持可见,这只是一个问题。

我尝试过的

如果我改变我的 notifyItemRemoved()调用 notifyDataSetChanged() ,新的最后一项将应用正确的项目偏移量,但我失去了从 notifyItemRemoved() 获得的内置动画.

如果我继续使用 notifyItemRemoved()并且另外调用notifyItemChanged()在前一个项目上,我会得到正确的项目偏移量,但动画有点卡顿;倒数第二个项目(在删除之前)似乎淡出然后再次出现(在此屏幕截图中,写着“项目 19”的卡片刚刚被删除):

enter image description here

我知道我可以通过对我的 <RecyclerView> 应用底部填充来在我的列表末尾创建一个大空间。标记并指定 android:clipToPadding="false" , 但由于各种原因,这种解决方案是 Not Acceptable 。

重现

我最初是在我正在开发的一个更大的应用程序中遇到这个问题的,但这里有一个演示该问题的小应用程序。

主要 Activity .java:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final MyAdapter adapter = new MyAdapter(20);

Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View view) {
adapter.removeItemAt(adapter.getItemCount() - 1);
}
});

RecyclerView recycler = findViewById(R.id.recycler);
recycler.setLayoutManager(new LinearLayoutManager(this));
recycler.addItemDecoration(new MyItemDecoration());
recycler.setAdapter(adapter);
}

private static class MyItemDecoration extends RecyclerView.ItemDecoration {

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int viewPosition = parent.getChildViewHolder(view).getLayoutPosition();
int lastPosition = state.getItemCount() - 1;

outRect.top = 20;
outRect.bottom = (viewPosition == lastPosition) ? 200 : 0;
}
}

private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

private final List<String> list;

private MyAdapter(int count) {
this.list = new ArrayList<>();

for (int i = 0; i < count; ++i) {
list.add("" + i);
}
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.item, parent, false);
return new MyViewHolder(itemView);
}

@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.text.setText("item: " + list.get(position));
}

@Override
public int getItemCount() {
return list.size();
}

private void removeItemAt(int position) {
if (list.size() > 0) {
list.remove(position);
notifyItemRemoved(position);
}
}
}

private static class MyViewHolder extends RecyclerView.ViewHolder {

private final TextView text;

public MyViewHolder(View itemView) {
super(itemView);
this.text = itemView.findViewById(R.id.text);
}
}
}

activity_main.xml:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#eee">

<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="REMOVE LAST ITEM"/>

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>

</LinearLayout>

项目.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp">

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_margin="8dp"
android:gravity="center"
android:textColor="#000"
android:textSize="16sp"/>

</android.support.v7.widget.CardView>

最佳答案

我通过更改 removeItemAt() 解决了这个问题方法如下:

private void removeItemAt(int position) {
if (list.size() > 0) {
list.remove(position);
notifyItemRemoved(position);

if (position != 0) {
notifyItemChanged(position - 1, Boolean.FALSE);
}
}
}

notifyItemChanged() 的第二个参数是关键。它实际上可以是任何对象,但我选择了 Boolean.FALSE因为它是一个“众所周知”的对象,并且因为它传达了一点 Intent :我不希望动画在我正在更改的项目上运行。

为什么有效

原来还有第二个onBindViewHolder() RecyclerView.Adapter 中定义的方法我以前从未见过。来自源代码:

public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
onBindViewHolder(holder, position);
}

摘自该方法的文档:

The payloads parameter is a merge list from notifyItemChanged(int, Object) or notifyItemRangeChanged(int, int, Object). If the payloads list is not empty, the ViewHolder is currently bound to old data and Adapter may run an efficient partial update using the payload info. If the payload is empty, Adapter must run a full bind.

换句话说,通过在 notifyItemChanged() 中传递某些东西(任何东西)作为有效负载,我们告诉系统我们只想执行“部分更新”(在可能的情况下)。

所以,当然,我们已经指示系统执行部分更新...但是这如何停止通过调用 notifyItemChanged(position - 1) 引起的闪烁呢? ?它与 RecyclerView.ItemAnimator 有关附加到所有 RecyclerView默认情况下:DefaultItemAnimator .

DefaultItemAnimator的源代码包括这个方法:

@Override
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}

您会注意到此方法还需要一个 List<Object> payloads范围。这与其他 onBindViewHolder() 中使用的有效负载列表相同。上面提到的调用。

因为我们传递了一个 payload 参数,这个方法将返回 true .由于动画师现在被告知可以重用已经存在的 ViewHolder对于我们的“已更改”项目,它不会将其拆除并创建一个新项目(或重复使用回收的项目)...这会停止运行已更改项目上的默认淡入淡出动画!

关于android - 从 RecyclerView.Adapter 中删除项目后,RecyclerView.ItemDecoration 不会更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47342034/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com