如何在回收器视图中删除动画时隐藏分隔符

Che*_*eng 22 android android-recyclerview

RecyclerView默认情况下,只要你提供一个很好的删除动画,setHasStableIds(true)并提供正确的实现getItemId.

最近,我RecyclerView通过/sf/answers/1892606131/添加了divider

结果如下

https://www.youtube.com/watch?v=u-2kPZwF_0w

https://youtu.be/c81OsFAL3zY(为了在删除动画播放时使分隔符更加清晰,我暂时将RecyclerView背景更改为红色)

当播放删除动画时,分隔符仍然可见.

但是,如果我查看GMail示例,当正在播放删除动画时,分隔线不再可见.它们被覆盖在纯色区域.

https://www.youtube.com/watch?v=cLs7paU-BIg

我可以知道,当删除动画播放时,如何通过不显示分隔线来实现与GMail相同的效果?

Dav*_*jak 29

解决方案相当容易.要为装饰制作动画,您可以而且应该使用view.getTranslation_()view.getAlpha().前段时间我在这个问题上写了一篇博文,你可以在这里阅读.

翻译和淡出

当添加或删除视图时,默认布局管理器将淡出视图(alpha)并翻译它们.你必须在装修中考虑到这一点.

这个想法很简单:

但是您绘制的装饰,通过使用应用相同的α和翻译到您的绘图view.getAlpha()view.getTranslationY().

根据您的链接答案,它必须如下调整:

// translate
int top = child.getBottom() + params.bottomMargin + view.getTranslationY();
int bottom = top + mDivider.getIntrinsicHeight();

// apply alpha
mDivider.setAlpha((int) child.getAlpha() * 255f);
mDivider.setBounds(left + view.getTranslationX(), top,
        right + view.getTranslationX(), bottom);
mDivider.draw(c);
Run Code Online (Sandbox Code Playgroud)

一个完整的样本

我喜欢自己绘制东西,因为我认为绘制一条线比布置一个drawable更少开销,这看起来如下所示:

public class SeparatorDecoration extends RecyclerView.ItemDecoration {

    private final Paint mPaint;
    private final int mAlpha;

    public SeparatorDecoration(@ColorInt int color, float width) {
        mPaint = new Paint();
        mPaint.setColor(color);
        mPaint.setStrokeWidth(width);
        mAlpha = mPaint.getAlpha();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();

        // we retrieve the position in the list
        final int position = params.getViewAdapterPosition();

        // add space for the separator to the bottom of every view but the last one
        if (position < state.getItemCount()) {
            outRect.set(0, 0, 0, (int) mPaint.getStrokeWidth()); // left, top, right, bottom
        } else {
            outRect.setEmpty(); // 0, 0, 0, 0
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        // a line will draw half its size to top and bottom,
        // hence the offset to place it correctly
        final int offset = (int) (mPaint.getStrokeWidth() / 2);

        // this will iterate over every visible view
        for (int i = 0; i < parent.getChildCount(); i++) {
            final View view = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();

            // get the position
            final int position = params.getViewAdapterPosition();

            // and finally draw the separator
            if (position < state.getItemCount()) {
                // apply alpha to support animations
                mPaint.setAlpha((int) (view.getAlpha() * mAlpha));

                float positionY = view.getBottom() + offset + view.getTranslationY();
                // do the drawing
                c.drawLine(view.getLeft() + view.getTranslationX(),
                        positionY,
                        view.getRight() + view.getTranslationX(),
                        positionY,
                        mPaint);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Kno*_*sos 7

首先,对于庞大的答案大小感到抱歉.但是,我认为有必要包括我的整个测试活动,以便您可以看到我做了什么.

问题

您遇到的问题是,您DividerItemDecoration不知道行的状态.它不知道该项目是否被删除.

出于这个原因,我创建了一个POJO,我们可以用它来包含一个整数(我们既用作itemId又用作可视化表示,并使用一个布尔值来表示该行是否被删除.

当您决定删除条目时(在此示例中adapter.notifyItemRangeRemoved(3, 8);),您还必须将关联设置Pojo为被删除(在此示例中pojo.beingDeleted = true;).

当被删除时,分隔符的位置将重置为父视图的颜色.为了掩盖分隔线.

我不太喜欢使用数据集本身来管理其父列表的状态.也许有更好的方法.

结果可视化

删除项目及其分隔符

活动:

public class MainActivity extends AppCompatActivity {
    private static final int VERTICAL_ITEM_SPACE = 8;

    private List<Pojo> mDataset = new ArrayList<Pojo>();

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

        for(int i = 0; i < 30; i++) {
            mDataset.add(new Pojo(i));
        }

        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
        recyclerView.addItemDecoration(new DividerItemDecoration(this));

        RecyclerView.ItemAnimator ia = recyclerView.getItemAnimator();
        ia.setRemoveDuration(4000);

        final Adapter adapter = new Adapter(mDataset);
        recyclerView.setAdapter(adapter);

        (new Handler(Looper.getMainLooper())).postDelayed(new Runnable() {
            @Override
            public void run() {
                int index = 0;
                Iterator<Pojo> it = mDataset.iterator();
                while(it.hasNext()) {
                    Pojo pojo = it.next();

                    if(index >= 3 && index <= 10) {
                        pojo.beingDeleted = true;
                        it.remove();
                    }

                    index++;
                }

                adapter.notifyItemRangeRemoved(3, 8);
            }
        }, 2000);
    }

    public class Adapter extends RecyclerView.Adapter<Holder> {
        private List<Pojo> mDataset;

        public Adapter(@NonNull final List<Pojo> dataset) {
            setHasStableIds(true);
            mDataset = dataset;
        }

        @Override
        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_cell, parent, false);
            return new Holder(view);
        }

        @Override
        public void onBindViewHolder(final Holder holder, final int position) {
            final Pojo data = mDataset.get(position);

            holder.itemView.setTag(data);
            holder.textView.setText("Test "+data.dataItem);
        }

        @Override
        public long getItemId(int position) {
            return mDataset.get(position).dataItem;
        }

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

    public class Holder extends RecyclerView.ViewHolder {
        public TextView textView;

        public Holder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.text);
        }
    }

    public class Pojo {
        public int dataItem;
        public boolean beingDeleted = false;

        public Pojo(int dataItem) {
            this.dataItem = dataItem;
        }
    }

    public class DividerItemDecoration extends RecyclerView.ItemDecoration {

        private final int[] ATTRS = new int[]{android.R.attr.listDivider};

        private Paint mOverwritePaint;
        private Drawable mDivider;

        /**
         * Default divider will be used
         */
        public DividerItemDecoration(Context context) {
            final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
            mDivider = styledAttributes.getDrawable(0);
            styledAttributes.recycle();
            initializePaint();
        }

        /**
         * Custom divider will be used
         */
        public DividerItemDecoration(Context context, int resId) {
            mDivider = ContextCompat.getDrawable(context, resId);
            initializePaint();
        }

        private void initializePaint() {
            mOverwritePaint = new Paint();
            mOverwritePaint.setColor(ContextCompat.getColor(MainActivity.this, android.R.color.background_light));
        }

        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            int left = parent.getPaddingLeft();
            int right = parent.getWidth() - parent.getPaddingRight();

            int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = parent.getChildAt(i);

                RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

                int top = child.getBottom() + params.bottomMargin;
                int bottom = top + mDivider.getIntrinsicHeight();

                Pojo item = (Pojo) child.getTag();
                if(item.beingDeleted) {
                    c.drawRect(left, top, right, bottom, mOverwritePaint);
                } else {
                    mDivider.setBounds(left, top, right, bottom);
                    mDivider.draw(c);
                }

            }
        }
    }

    public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {

        private final int mVerticalSpaceHeight;

        public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
            this.mVerticalSpaceHeight = mVerticalSpaceHeight;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                                   RecyclerView.State state) {
            outRect.bottom = mVerticalSpaceHeight;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

活动布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:background="@android:color/background_light"
    tools:context="test.dae.myapplication.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)

RecyclerView"行"布局

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:id="@+id/text"
          android:padding="8dp">

</TextView>
Run Code Online (Sandbox Code Playgroud)