android 如何在RecyclerView中更新完成动画后的项目?

nhaq1z21  于 2023-06-20  发布在  Android
关注(0)|答案(1)|浏览(81)

我使用recyclerView以一种很好的方式显示数据库中的所有条目。我添加了SwipeToDeleteCallback类,以提供从recyclerView中删除项目的能力。当删除项时,recyclerview向上提升下一项。
每个项目包含2个textView,在recyclerView适配器中的onBindViewHolder()中更新。问题是当我删除一些项目时,屏幕上出现的最后一个项目没有正确显示这些textView。我已经尝试了adapter.notifyItemChanged()和view.postInvalidate(),但没有用。
看起来如果recyclerView试图在动画期间更新其子对象的值,它会产生这样的错误。我不确定我说的对不对。
Screenshot: After deleting a few previous item two last items doesn't appear properly
DictionaryActivity.kt:

class DictionaryActivity : AppCompatActivity() {
    private lateinit var coordinatorLayout : CoordinatorLayout
    private lateinit var recyclerView : RecyclerView
    private lateinit var dictionaryAdapter: DictionaryAdapter
    private lateinit var linearLayoutManager: LinearLayoutManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.dictionary)

        coordinatorLayout = findViewById<View>(R.id.dictionaryCoordinatorLayout) as CoordinatorLayout
        recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView

        // Create adapter passing in the sample user data
        dictionaryAdapter = DictionaryAdapter(Database.getAllEntries(this))
        // Attach the adapter to the recyclerview to populate items
        recyclerView.adapter = dictionaryAdapter
        // Set layout manager to position the items
        linearLayoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = linearLayoutManager

        enableSwipeToDeleteAndUndo()
    }

    private fun enableSwipeToDeleteAndUndo() {
        val swipeToDeleteCallback: SwipeToDeleteCallback = object : SwipeToDeleteCallback(this) {
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) {
                val position = viewHolder.adapterPosition
                val entry: Entry = dictionaryAdapter.entries[position]
                dictionaryAdapter.removeEntry(position)
                val snackbar = Snackbar
                    .make(
                        coordinatorLayout,
                        "Item was removed from the list.",
                        Snackbar.LENGTH_LONG
                    )
                snackbar.setAction("UNDO") {
                    dictionaryAdapter.restoreEntry(entry, position)
                    recyclerView.scrollToPosition(position)
                }
               
                snackbar.setActionTextColor(Color.YELLOW)
                snackbar.show()
            }
        }
        val itemTouchhelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchhelper.attachToRecyclerView(recyclerView)
    }
}

SwipeToDeleteCallback.java:

abstract public class SwipeToDeleteCallback extends ItemTouchHelper.Callback {

    Context mContext;
    private Paint clearPaint;
    private ColorDrawable background;
    private int backgroundColor;
    private Drawable deleteDrawable;
    private int intrinsicWidth;
    private int intrinsicHeight;

    SwipeToDeleteCallback(Context context) {
        mContext = context;
        background = new ColorDrawable();
        backgroundColor = Color.parseColor("#b80f0a");
        clearPaint = new Paint();
        clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        deleteDrawable = ContextCompat.getDrawable(mContext, R.drawable.ic_delete);
        intrinsicWidth = deleteDrawable.getIntrinsicWidth();
        intrinsicHeight = deleteDrawable.getIntrinsicHeight();

    }

    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        return makeMovementFlags(0, ItemTouchHelper.LEFT);
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
        return false;
    }

    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        View itemView = viewHolder.itemView;
        int itemHeight = itemView.getHeight();

        boolean isCancelled = dX == 0 && !isCurrentlyActive;

        if (isCancelled) {
            clearCanvas(c, itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            return;
        }

        background.setColor(backgroundColor);
        background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
        background.draw(c);

        int deleteIconTop = itemView.getTop() + (itemHeight - intrinsicHeight) / 2;
        int deleteIconMargin = (itemHeight - intrinsicHeight) / 2;
        int deleteIconLeft = itemView.getRight() - deleteIconMargin - intrinsicWidth;
        int deleteIconRight = itemView.getRight() - deleteIconMargin;
        int deleteIconBottom = deleteIconTop + intrinsicHeight;

        deleteDrawable.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom);
        deleteDrawable.draw(c);

        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

    }

    private void clearCanvas(Canvas c, Float left, Float top, Float right, Float bottom) {
        c.drawRect(left, top, right, bottom, clearPaint);

    }

    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return 0.7f;
    }
}

DictionaryAdapter.java:

public class DictionaryAdapter  extends
        RecyclerView.Adapter<DictionaryAdapter.ViewHolder> {

    // Provide a direct reference to each of the views within a data item
    // Used to cache the views within the item layout for fast access
    public class ViewHolder extends RecyclerView.ViewHolder {
        // Your holder should contain a member variable
        // for any view that will be set as you render a row
        private final TextView wordRussianTextView;
        private final  TextView wordHebrewTextView;

        private final  ProgressBar progressBar;
        private final  ImageButton editButton;

        // We also create a constructor that accepts the entire item row
        // and does the view lookups to find each subview
        public ViewHolder(View itemView) {
            // Stores the itemView in a public final member variable that can be used
            // to access the context from any ViewHolder instance.
            super(itemView);

            wordRussianTextView = (TextView) itemView.findViewById(R.id.textView7);
            wordHebrewTextView = (TextView)  itemView.findViewById(R.id.textView8);
            progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar2);
            editButton  = (ImageButton) itemView.findViewById(R.id.imageButton2);

        }
    }

    private List<Entry> entries;

    // Pass in the contact array into the constructor
    public DictionaryAdapter(List<Entry> entries) {
        this.entries = entries;
    }

    // Usually involves inflating a layout from XML and returning the holder
    @NonNull
    @Override
    public DictionaryAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);

        // Inflate the custom layout
        View contactView = inflater.inflate(R.layout.word_element, parent, false);

        // Return a new holder instance
        ViewHolder viewHolder = new ViewHolder(contactView);

        return viewHolder;
    }

    // Involves populating data into the item through holder
    @Override
    public void onBindViewHolder(DictionaryAdapter.ViewHolder holder, int position) {
        // Get the data model based on position
        Entry entry = entries.get(position);

        // Set item views based on your views and data model
        holder.wordRussianTextView.setText(entry.getWord(Entry.LANGUAGE.RUSSIAN));
        holder.wordHebrewTextView.setText(entry.getWord(Entry.LANGUAGE.HEBREW));
        holder.progressBar.setProgress(entry.getProgress());

        Log.e("onBindViewHolder", "New item:");
        Log.e("onBindViewHolder", "russian : " + holder.wordRussianTextView.getText());
        Log.e("onBindViewHolder", "hebrew : " + holder.wordHebrewTextView.getText());
        Log.e("onBindViewHolder", "--------------------------------");

        //holder.wordRussianTextView
    }

    // Returns the total count of items in the list
    @Override
    public int getItemCount() {
        return entries.size();
    }

    public void removeEntry(int position) {
        entries.remove(position);
        notifyItemRemoved(position);
    }

    public void restoreEntry(Entry entry, int position) {
        entries.add(position, entry);
        notifyItemInserted(position);
    }

    public List<Entry> getEntries() {
        return entries;
    }
}
2sbarzqh

2sbarzqh1#

我自己找到了答案。出于某种原因,我将textViews的参数layout_width设置为0dp而不是wrap_content。我改变了值,一切都开始正常工作。

相关问题