实现GridLayoutManager支持RTL

来源:互联网 发布:知乎 自己拍婚纱照 编辑:程序博客网 时间:2024/06/05 22:50

最近在工作中遇见个奇怪现象,即RecyclerView 采用GridLayoutManager进行布局,当系统是LTR时,数据刷新时变现正常;但是如果是RTL情况下,每次数据添加或莫名其妙向上滚动一个单元格。测试提出这个问题后就开始疯狂找问题,最终定位是由于GridLayoutManager内部方法findReferenceChild引起的。

GridLayoutManager源码如下:

View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state,                        int start, int end, int itemCount) {    ensureLayoutState();    View invalidMatch = null;    View outOfBoundsMatch = null;    final int boundsStart = mOrientationHelper.getStartAfterPadding();    final int boundsEnd = mOrientationHelper.getEndAfterPadding();    final int diff = end > start ? 1 : -1;    for (int i = start; i != end; i += diff) {        final View view = getChildAt(i);        final int position = getPosition(view);        if (position >= 0 && position < itemCount) {            final int span = getSpanIndex(recycler, state, position);            if (span != 0) {                continue;            }            if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {                if (invalidMatch == null) {                    invalidMatch = view; // removed item, least preferred                }            } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||                    mOrientationHelper.getDecoratedEnd(view) < boundsStart) {                if (outOfBoundsMatch == null) {                    outOfBoundsMatch = view; // item is not visible, less preferred                }            } else {                return view;            }        }    }    return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;}

其中:

final int span = getSpanIndex(recycler, state, position);            if (span != 0) {                continue;            }

span != 0 这个地方有问题,因为发现RTL时取到的值并不是最后显示的view对应的position。找到问题后果断采取措施,重写该方法。

但是发现这个这个方法是包内可见的方法,于是定义类生命包名跟GridLayoutManager包名相同,重新方法findReferenceChild,当RTL时获取spanIndex=mSpanCount-1;

源码如下:

package android.support.v7.widget;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by liuyt on 17-5-9. * to make {@link GridLayoutManager} support RTL , * with fix bug of it that not support RTL very well */public class RTLGridLayoutManager extends GridLayoutManager {    private static final boolean DEBUG = false;    private static final String TAG = "MyGridLayoutManager";    /**     * Constructor used when layout manager is set in XML by RecyclerView attribute     * "layoutManager". If spanCount is not specified in the XML, it defaults to a     * single column.     *     * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount     */    public RTLGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,                                int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);    }    /**     * Creates a vertical GridLayoutManager     *     * @param context   Current context, will be used to access resources.     * @param spanCount The number of columns in the grid     */    public RTLGridLayoutManager(Context context, int spanCount) {        super(context, spanCount);    }    /**     * @param context       Current context, will be used to access resources.     * @param spanCount     The number of columns or rows in the grid     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link     *                      #VERTICAL}.     * @param reverseLayout When set to true, layouts from end to start.     */    public RTLGridLayoutManager(Context context, int spanCount, int orientation,                                boolean reverseLayout) {        super(context, spanCount, orientation, reverseLayout);    }    @Override    View findReferenceChild(RecyclerView.Recycler recycler, RecyclerView.State state, int start, int end, int itemCount) {        ensureLayoutState();        View invalidMatch = null;        View outOfBoundsMatch = null;        final int boundsStart = mOrientationHelper.getStartAfterPadding();        final int boundsEnd = mOrientationHelper.getEndAfterPadding();        final int diff = end > start ? 1 : -1;        int index;        if (isLayoutRTL()) {            index = mSpanCount - 1;        } else {            index = 0;        }        for (int i = start; i != end; i += diff) {            final View view = getChildAt(i);            final int position = getPosition(view);            if (position >= 0 && position < itemCount) {                final int span = getSpanIndex(recycler, state, position);                if (span != index) {                    continue;                }                if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {                    if (invalidMatch == null) {                        invalidMatch = view; // removed item, least preferred                    }                } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||                        mOrientationHelper.getDecoratedEnd(view) < boundsStart) {                    if (outOfBoundsMatch == null) {                        outOfBoundsMatch = view; // item is not visible, less preferred                    }                } else {                    return view;                }            }        }        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;    }    private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {        if (!state.isPreLayout()) {            return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);        }        final int cached = mPreLayoutSpanIndexCache.get(pos, -1);        if (cached != -1) {            return cached;        }        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);        if (adapterPosition == -1) {            if (DEBUG) {                throw new RuntimeException("Cannot find span index for pre layout position. It is"                        + " not cached, not in the adapter. Pos:" + pos);            }            Log.w(TAG, "Cannot find span size for pre layout position. It is"                    + " not cached, not in the adapter. Pos:" + pos);            return 0;        }        return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);    }}


0 0
原创粉丝点击