reciclapp reciclaje moviles funciona como bicicla aplicaciones android android-recyclerview

android - reciclaje - como funciona reciclapp



Cómo ocultar el divisor cuando la animación de eliminación ocurre en la vista de reciclador (3)

Creo que el ItemDecorator que usas para dibujar un divisor después de cada fila está arruinando las cosas cuando se realiza el barrido para eliminar.

En lugar de utilizar ItemDecorator para dibujar un divisor en una vista de reciclaje, agregue una vista al final de su diseño de diseño secundario RecyclerView. Que dibujará una línea divisoria como ItemDecorator.

<LinearLayout 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" > <!-- child layout Design !--> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@android:color/darker_gray" android:layout_gravity="bottom" /> </Linearlayout>

RecyclerView de forma predeterminada, viene con una animación de eliminación agradable, siempre que setHasStableIds(true) y proporcione la implementación correcta en getItemId .

Recientemente, he añadido divisor en RecyclerView través de https://stackoverflow.com/a/27037230/72437

El resultado se ve como sigue

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

https://youtu.be/c81OsFAL3zY (Para hacer que los divisores sean más visibles cuando se reproduce la animación eliminada, cambio temporalmente el fondo RecyclerView a rojo)

Los divisores siguen visibles cuando se reproduce la animación de eliminación.

Sin embargo, si miro el ejemplo de GMail, cuando se reproduce la animación de eliminación, las líneas divisorias ya no son visibles. Están siendo cubiertos un área de color sólido.

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

¿Puedo saber cómo puedo lograr el mismo efecto que GMail, al no mostrar líneas divisorias cuando se reproduce la animación de eliminación?


En primer lugar, lo siento por el tamaño de la respuesta masiva. Sin embargo, sentí que era necesario incluir toda la actividad de prueba para que pueda ver lo que he hecho.

La cuestión

El problema que tiene, es que DividerItemDecoration no tiene idea del estado de su fila. No sabe si el elemento está siendo eliminado.

Por esta razón, hice un POJO que podemos usar para contener un entero (que usamos como itemId y una representación visual y un booleano que indica que esta fila se está eliminando o no).

Cuando decide eliminar las entradas (en este ejemplo, adapter.notifyItemRangeRemoved(3, 8); ), también debe configurar el Pojo asociado para que se elimine (en este ejemplo, pojo.beingDeleted = true; ).

La posición del divisor cuando se elimina, se restablece al color de la vista principal. Para cubrir el divisor.

No me gusta mucho usar el conjunto de datos para administrar el estado de su lista principal. Quizás haya una mejor manera.

El resultado visualizado.

La actividad:

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; } } }

El diseño de la actividad

<?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>

El diseño de la "fila" 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>


La solución es bastante fácil. Para animar una decoración, puede y debe usar view.getTranslation_() y view.getAlpha() . Hace un tiempo escribí una publicación en el blog sobre este tema exacto, puede leerlo here .

Traducción y desvanecimiento

El administrador de diseño predeterminado desvanecerá las vistas (alfa) y las traducirá, cuando se agreguen o eliminen. Tienes que tener esto en cuenta en tu decoración.

La idea es simple:

Sin embargo, usted dibuja su decoración, aplica el mismo alfa y la traducción a su dibujo utilizando view.getAlpha() y view.getTranslationY() .

Siguiendo tu respuesta vinculada, tendría que ser adaptada como la siguiente:

// 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);

Una muestra completa

Me gusta dibujar cosas por mí mismo, ya que creo que dibujar una línea es menos costoso que diseñar un dibujable, esto sería lo siguiente:

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); } } } }