Android

Android RecyclerView.ViewHolder

2018-05-08  本文已影响27人  ThrowException

记录一次自己遇到的问题,如果我是错误的请留言告知我一下,避免误导别人[掩脸]

在 ViewHolder 中处理 item 的点击事件的时候,发现多个 item 同时点击就会出现闪退,debug 看到 position = -1.

出现原因

我在 ViewHolder 处理点击事件的时候,是通过 ViewHolder#getAdapterPosition() 来获取 position 的。查看源码发现,这个方法获取的 position 是不靠谱的。下面是源码:

/**
 * Returns the Adapter position of the item represented by this ViewHolder.
 * <p>
 * Note that this might be different than the {@link #getLayoutPosition()} if there are
 * pending adapter updates but a new layout pass has not happened yet.
 * <p>
 * RecyclerView does not handle any adapter updates until the next layout traversal. This
 * may create temporary inconsistencies between what user sees on the screen and what
 * adapter contents have. This inconsistency is not important since it will be less than
 * 16ms but it might be a problem if you want to use ViewHolder position to access the
 * adapter. Sometimes, you may need to get the exact adapter position to do
 * some actions in response to user events. In that case, you should use this method which
 * will calculate the Adapter position of the ViewHolder.
 * <p>
 * Note that if you've called {@link RecyclerView.Adapter#notifyDataSetChanged()}, until the
 * next layout pass, the return value of this method will be {@link #NO_POSITION}.
 *
 * @return The adapter position of the item if it still exists in the adapter.
 * {@link RecyclerView#NO_POSITION} if item has been removed from the adapter,
 * {@link RecyclerView.Adapter#notifyDataSetChanged()} has been called after the last
 * layout pass or the ViewHolder has already been recycled.
 */
public final int getAdapterPosition() {
    if (mOwnerRecyclerView == null) {
        return NO_POSITION;
    }
    return mOwnerRecyclerView.getAdapterPositionFor(this);
}

解决方案

使用 ViewHolder#getLayoutPosition() 获取 position。下面是源码:

/**
 * Returns the position of the ViewHolder in terms of the latest layout pass.
 * <p>
 * This position is mostly used by RecyclerView components to be consistent while
 * RecyclerView lazily processes adapter updates.
 * <p>
 * For performance and animation reasons, RecyclerView batches all adapter updates until the
 * next layout pass. This may cause mismatches between the Adapter position of the item and
 * the position it had in the latest layout calculations.
 * <p>
 * LayoutManagers should always call this method while doing calculations based on item
 * positions. All methods in {@link RecyclerView.LayoutManager}, {@link RecyclerView.State},
 * {@link RecyclerView.Recycler} that receive a position expect it to be the layout position
 * of the item.
 * <p>
 * If LayoutManager needs to call an external method that requires the adapter position of
 * the item, it can use {@link #getAdapterPosition()} or
 * {@link RecyclerView.Recycler#convertPreLayoutPositionToPostLayout(int)}.
 *
 * @return Returns the adapter position of the ViewHolder in the latest layout pass.
 * @see #getAdapterPosition()
 */
public final int getLayoutPosition() {
    return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
}
上一篇 下一篇

猜你喜欢

热点阅读