Recycleview Scroll Issue Диспетчер пользовательских макетов с NestedScrollView - PullRequest
0 голосов
/ 13 января 2019

Я использую recycleview nestedscrollview.Я использую customlayoutmanager для дизайна recycleview. Я хочу закрыть функцию прокрутки в Recycleview и использовать только представление nestedscroll. recycleview.setnestedscrollingenabled (false); Прокрутка не работает вообще, когда я вставляю код. Когда я добавляю setNestedScrollingEnabled (true), только функция прокрутки recycleview работает. Как я могу просто сделать функцию прокрутки nestedscrollview?

XML

<android.support.v4.widget.NestedScrollView
android:background="@android:color/white"
android:clipToPadding="true"
android:descendantFocusability="blocksDescendants"
android:fillViewport="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_height="100dp"
android:layout_margin="20dp"
android:layout_width="match_parent"
android:padding="10dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

<android.support.v7.widget.RecyclerView

    android:id="@+id/recycleView"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:nestedScrollingEnabled="false"
    android:overScrollMode="never"
    android:scrollbars="none"

    />

CustomLayoutManager

public class SpannedGridLayoutManager extends RecyclerView.LayoutManager {

private GridSpanLookup spanLookup;
private int columns = 1;
private float cellAspectRatio = 1f;

private int cellHeight;
private int[] cellBorders;
private int firstVisiblePosition;
private int lastVisiblePosition;
private int firstVisibleRow;
private int lastVisibleRow;
private boolean forceClearOffsets;
private SparseArray<GridCell> cells;
private List<Integer> firstChildPositionForRow;
private int totalRows;
private final Rect itemDecorationInsets = new Rect();

public SpannedGridLayoutManager(GridSpanLookup spanLookup, int columns, float cellAspectRatio) {
    this.spanLookup = spanLookup;
    this.columns = columns;
    this.cellAspectRatio = cellAspectRatio;
    setAutoMeasureEnabled(true);
}

@Keep
public SpannedGridLayoutManager(
        Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    TypedArray a = context.obtainStyledAttributes(
            attrs, R.styleable.SpannedGridLayoutManager, defStyleAttr, defStyleRes);
    columns = a.getInt(R.styleable.SpannedGridLayoutManager_spanCount, 1);
    parseAspectRatio(a.getString(R.styleable.SpannedGridLayoutManager_aspectRatio));
    a.recycle();
    setAutoMeasureEnabled(true);
}

public interface GridSpanLookup {
    SpanInfo getSpanInfo(int position);
}

public void setSpanLookup(@NonNull GridSpanLookup spanLookup) {
    this.spanLookup = spanLookup;
}

public static class SpanInfo {
    public int columnSpan;
    public int rowSpan;

    public SpanInfo(int columnSpan, int rowSpan) {
        this.columnSpan = columnSpan;
        this.rowSpan = rowSpan;
    }

    public static final SpanInfo SINGLE_CELL = new SpanInfo(1, 1);
}

public static class LayoutParams extends RecyclerView.LayoutParams {

    int columnSpan;
    int rowSpan;

    public LayoutParams(Context c, AttributeSet attrs) {
        super(c, attrs);
    }

    public LayoutParams(int width, int height) {
        super(width, height);
    }

    public LayoutParams(ViewGroup.MarginLayoutParams source) {
        super(source);
    }

    public LayoutParams(ViewGroup.LayoutParams source) {
        super(source);
    }

    public LayoutParams(RecyclerView.LayoutParams source) {
        super(source);
    }
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    calculateWindowSize();
    calculateCellPositions(recycler, state);

    if (state.getItemCount() == 0) {
        detachAndScrapAttachedViews(recycler);
        firstVisibleRow = 0;
        resetVisibleItemTracking();
        return;
    }

    // TODO use orientationHelper
    int startTop = getPaddingTop();
    int scrollOffset = 0;
    if (forceClearOffsets) {
        startTop = -(firstVisibleRow * cellHeight);
        forceClearOffsets = false;
    } else if (getChildCount() != 0) {
        scrollOffset = getDecoratedTop(getChildAt(0));
        startTop = scrollOffset - (firstVisibleRow * cellHeight);
        resetVisibleItemTracking();
    }

    detachAndScrapAttachedViews(recycler);
    int row = firstVisibleRow;
    int availableSpace = getHeight() - scrollOffset;
    int lastItemPosition = state.getItemCount() - 1;
    while (availableSpace > 0 && lastVisiblePosition < lastItemPosition) {
        availableSpace -= layoutRow(row, startTop, recycler, state);
        row = getNextSpannedRow(row);
    }

    layoutDisappearingViews(recycler, state, startTop);
}

@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT);
}

@Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
    return new LayoutParams(c, attrs);
}

@Override
public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
    if (lp instanceof ViewGroup.MarginLayoutParams) {
        return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
    } else {
        return new LayoutParams(lp);
    }
}

@Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
    return lp instanceof LayoutParams;
}

@Override
public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
    removeAllViews();
    reset();
}

@Override
public boolean supportsPredictiveItemAnimations() {
    return true;
}



@Override
public boolean canScrollHorizontally() {
    return false;
}

@Override
public boolean canScrollVertically() {
    return false;
}

@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (getChildCount() == 0 || dy == 0) return 0;

    int scrolled;
    int top = getDecoratedTop(getChildAt(0));

    if (dy < 0) {
        if (firstVisibleRow == 0) {
            int scrollRange = -(getPaddingTop() - top);
            scrolled = Math.max(dy, scrollRange);
        } else {
            scrolled = dy;
        }
        if (top - scrolled >= 0) {
            int newRow = firstVisibleRow - 1;
            if (newRow >= 0) {
                int startOffset = top - (firstVisibleRow * cellHeight);
                layoutRow(newRow, startOffset, recycler, state);
            }
        }
        int firstPositionOfLastRow = getFirstPositionInSpannedRow(lastVisibleRow);
        int lastRowTop = getDecoratedTop(
                getChildAt(firstPositionOfLastRow - firstVisiblePosition));
        if (lastRowTop - scrolled > getHeight()) {
            recycleRow(lastVisibleRow, recycler, state);
        }
    } else {
        int bottom = getDecoratedBottom(getChildAt(getChildCount() - 1));
        if (lastVisiblePosition == getItemCount() - 1) {
            int scrollRange = Math.max(bottom - getHeight() + getPaddingBottom(), 0);
            scrolled = Math.min(dy, scrollRange);
        } else {
            scrolled = dy;
        }
        if ((bottom - scrolled) < getHeight()) {
            int nextRow = lastVisibleRow + 1;
            if (nextRow < getSpannedRowCount()) {
                int startOffset = top - (firstVisibleRow * cellHeight);
                layoutRow(nextRow, startOffset, recycler, state);
            }
        }
        int lastPositionInRow = getLastPositionInSpannedRow(firstVisibleRow, state);
        int bottomOfFirstRow =
                getDecoratedBottom(getChildAt(lastPositionInRow - firstVisiblePosition));
        if (bottomOfFirstRow - scrolled < 0) {
            recycleRow(firstVisibleRow, recycler, state);
        }
    }
    offsetChildrenVertical(-scrolled);
    return scrolled;
}

@Override
public void scrollToPosition(int position) {
    if (position >= getItemCount()) position = getItemCount() - 1;

    firstVisibleRow = getRowIndex(position);
    resetVisibleItemTracking();
    forceClearOffsets = true;
    removeAllViews();
    requestLayout();
}

@Override
public void smoothScrollToPosition(
        RecyclerView recyclerView, RecyclerView.State state, int position) {
    if (position >= getItemCount()) position = getItemCount() - 1;

    LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) {
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            final int rowOffset = getRowIndex(targetPosition) - firstVisibleRow;
            return new PointF(0, rowOffset * cellHeight);
        }
    };
    scroller.setTargetPosition(position);
    startSmoothScroll(scroller);
}

@Override
public int computeVerticalScrollRange(RecyclerView.State state) {
    // TODO update this to incrementally calculate
    return getSpannedRowCount() * cellHeight + getPaddingTop() + getPaddingBottom();
}

@Override
public int computeVerticalScrollExtent(RecyclerView.State state) {
    return getHeight();
}

@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
    if (getChildCount() == 0) return 0;
    return getPaddingTop() + (firstVisibleRow * cellHeight) - getDecoratedTop(getChildAt(0));
}

@Override
public View findViewByPosition(int position) {
    if (position < firstVisiblePosition || position > lastVisiblePosition) return null;
    return getChildAt(position - firstVisiblePosition);
}

public int getFirstVisibleItemPosition() {
    return firstVisiblePosition;
}

private static class GridCell {
    final int row;
    final int rowSpan;
    final int column;
    final int columnSpan;

    GridCell(int row, int rowSpan, int column, int columnSpan) {
        this.row = row;
        this.rowSpan = rowSpan;
        this.column = column;
        this.columnSpan = columnSpan;
    }
}


private void calculateCellPositions(RecyclerView.Recycler recycler, RecyclerView.State state) {
    final int itemCount = state.getItemCount();
    cells = new SparseArray<>(itemCount);
    firstChildPositionForRow = new ArrayList<>();
    int row = 0;
    int column = 0;
    recordSpannedRowStartPosition(row, column);
    int[] rowHWM = new int[columns]; // row high water mark (per column)
    for (int position = 0; position < itemCount; position++) {

        SpanInfo spanInfo;
        int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(position);
        if (adapterPosition != RecyclerView.NO_POSITION) {
            spanInfo = spanLookup.getSpanInfo(adapterPosition);
        } else {

            spanInfo = getSpanInfoFromAttachedView(position);
        }

        if (spanInfo.columnSpan > columns) {
            spanInfo.columnSpan = columns; // or should we throw?
        }


        if (column + spanInfo.columnSpan > columns) {
            row++;
            recordSpannedRowStartPosition(row, position);
            column = 0;
        }


        while (rowHWM[column] > row) {
            column++;
            if (column + spanInfo.columnSpan > columns) {
                row++;
                recordSpannedRowStartPosition(row, position);
                column = 0;
            }
        }


        cells.put(position, new GridCell(row, spanInfo.rowSpan, column, spanInfo.columnSpan));


        for (int columnsSpanned = 0; columnsSpanned < spanInfo.columnSpan; columnsSpanned++) {
            rowHWM[column + columnsSpanned] = row + spanInfo.rowSpan;
        }

        if (spanInfo.rowSpan > 1) {
            int rowStartPosition = getFirstPositionInSpannedRow(row);
            for (int rowsSpanned = 1; rowsSpanned < spanInfo.rowSpan; rowsSpanned++) {
                int spannedRow = row + rowsSpanned;
                recordSpannedRowStartPosition(spannedRow, rowStartPosition);
            }
        }


        column += spanInfo.columnSpan;
    }
    totalRows = rowHWM[0];
    for (int i = 1; i < rowHWM.length; i++) {
        if (rowHWM[i] > totalRows) {
            totalRows = rowHWM[i];
        }
    }
}

private SpanInfo getSpanInfoFromAttachedView(int position) {
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        if (position == getPosition(child)) {
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            return new SpanInfo(lp.columnSpan, lp.rowSpan);
        }
    }

    return SpanInfo.SINGLE_CELL;
}

private void recordSpannedRowStartPosition(final int rowIndex, final int position) {
    if (getSpannedRowCount() < (rowIndex + 1)) {
        firstChildPositionForRow.add(position);
    }
}

private int getRowIndex(final int position) {
    return position < cells.size() ? cells.get(position).row : -1;
}

private int getSpannedRowCount() {
    return firstChildPositionForRow.size();
}

private int getNextSpannedRow(int rowIndex) {
    int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
    int nextRow = rowIndex + 1;
    while (nextRow < getSpannedRowCount()
            && getFirstPositionInSpannedRow(nextRow) == firstPositionInRow) {
        nextRow++;
    }
    return nextRow;
}

private int getFirstPositionInSpannedRow(int rowIndex) {
    return firstChildPositionForRow.get(rowIndex);
}

private int getLastPositionInSpannedRow(final int rowIndex, RecyclerView.State state) {
    int nextRow = getNextSpannedRow(rowIndex);
    return (nextRow != getSpannedRowCount()) ? // check if reached boundary
            getFirstPositionInSpannedRow(nextRow) - 1
            : state.getItemCount() - 1;
}


private int layoutRow(
        int rowIndex, int startTop, RecyclerView.Recycler recycler, RecyclerView.State state) {
    int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
    int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
    boolean containsRemovedItems = false;

    int insertPosition = (rowIndex < firstVisibleRow) ? 0 : getChildCount();
    for (int position = firstPositionInRow;
         position <= lastPositionInRow;
         position++, insertPosition++) {

        View view = recycler.getViewForPosition(position);
        LayoutParams lp = (LayoutParams) view.getLayoutParams();
        containsRemovedItems |= lp.isItemRemoved();
        GridCell cell = cells.get(position);
        addView(view, insertPosition);

        // TODO use orientation helper
        int wSpec = getChildMeasureSpec(
                cellBorders[cell.column + cell.columnSpan] - cellBorders[cell.column],
                View.MeasureSpec.EXACTLY, 0, lp.width, false);
        int hSpec = getChildMeasureSpec(cell.rowSpan * cellHeight,
                View.MeasureSpec.EXACTLY, 0, lp.height, true);
        measureChildWithDecorationsAndMargin(view, wSpec, hSpec);

        int left = cellBorders[cell.column] + lp.leftMargin;
        int top = startTop + (cell.row * cellHeight) + lp.topMargin;
        int right = left + getDecoratedMeasuredWidth(view);
        int bottom = top + getDecoratedMeasuredHeight(view);
        layoutDecorated(view, left, top, right, bottom);
        lp.columnSpan = cell.columnSpan;
        lp.rowSpan = cell.rowSpan;
    }

    if (firstPositionInRow < firstVisiblePosition) {
        firstVisiblePosition = firstPositionInRow;
        firstVisibleRow = getRowIndex(firstVisiblePosition);
    }
    if (lastPositionInRow > lastVisiblePosition) {
        lastVisiblePosition = lastPositionInRow;
        lastVisibleRow = getRowIndex(lastVisiblePosition);
    }
    if (containsRemovedItems) return 0;

    GridCell first = cells.get(firstPositionInRow);
    GridCell last = cells.get(lastPositionInRow);
    return (last.row + last.rowSpan - first.row) * cellHeight;
}


private void recycleRow(
        int rowIndex, RecyclerView.Recycler recycler, RecyclerView.State state) {
    int firstPositionInRow = getFirstPositionInSpannedRow(rowIndex);
    int lastPositionInRow = getLastPositionInSpannedRow(rowIndex, state);
    int toRemove = lastPositionInRow;
    while (toRemove >= firstPositionInRow) {
        int index = toRemove - firstVisiblePosition;
        removeAndRecycleViewAt(index, recycler);
        toRemove--;
    }
    if (rowIndex == firstVisibleRow) {
        firstVisiblePosition = lastPositionInRow + 1;
        firstVisibleRow = getRowIndex(firstVisiblePosition);
    }
    if (rowIndex == lastVisibleRow) {
        lastVisiblePosition = firstPositionInRow - 1;
        lastVisibleRow = getRowIndex(lastVisiblePosition);
    }
}

private void layoutDisappearingViews(
        RecyclerView.Recycler recycler, RecyclerView.State state, int startTop) {
    // TODO
}

private void calculateWindowSize() {
    // TODO use OrientationHelper#getTotalSpace
    int cellWidth =
            (int) Math.floor((getWidth() - getPaddingLeft() - getPaddingRight()) / columns);
    cellHeight = (int) Math.floor(cellWidth * (1f / cellAspectRatio));
    calculateCellBorders();
}

private void reset() {
    cells = null;
    firstChildPositionForRow = null;
    firstVisiblePosition = 0;
    firstVisibleRow = 0;
    lastVisiblePosition = 0;
    lastVisibleRow = 0;
    cellHeight = 0;
    forceClearOffsets = false;
}

private void resetVisibleItemTracking() {

    // TODO make orientation agnostic
    int minimumVisibleRow = getMinimumFirstVisibleRow();
    if (firstVisibleRow > minimumVisibleRow) firstVisibleRow = minimumVisibleRow;
    firstVisiblePosition = getFirstPositionInSpannedRow(firstVisibleRow);
    lastVisibleRow = firstVisibleRow;
    lastVisiblePosition = firstVisiblePosition;
}

private int getMinimumFirstVisibleRow() {
    int maxDisplayedRows = (int) Math.ceil((float) getHeight() / cellHeight) + 1;
    if (totalRows < maxDisplayedRows) return 0;
    int minFirstRow = totalRows - maxDisplayedRows;

    return getRowIndex(getFirstPositionInSpannedRow(minFirstRow));
}



private void calculateCellBorders() {
    cellBorders = new int[columns + 1];
    int totalSpace = getWidth() - getPaddingLeft() - getPaddingRight();
    int consumedPixels = getPaddingLeft();
    cellBorders[0] = consumedPixels;
    int sizePerSpan = totalSpace / columns;
    int sizePerSpanRemainder = totalSpace % columns;
    int additionalSize = 0;
    for (int i = 1; i <= columns; i++) {
        int itemSize = sizePerSpan;
        additionalSize += sizePerSpanRemainder;
        if (additionalSize > 0 && (columns - additionalSize) < sizePerSpanRemainder) {
            itemSize += 1;
            additionalSize -= columns;
        }
        consumedPixels += itemSize;
        cellBorders[i] = consumedPixels;
    }
}

private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
    calculateItemDecorationsForChild(child, itemDecorationInsets);
    RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
    widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + itemDecorationInsets.left,
            lp.rightMargin + itemDecorationInsets.right);
    heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + itemDecorationInsets.top,
            lp.bottomMargin + itemDecorationInsets.bottom);
    child.measure(widthSpec, heightSpec);
}

private int updateSpecWithExtra(int spec, int startInset, int endInset) {
    if (startInset == 0 && endInset == 0) {
        return spec;
    }
    int mode = View.MeasureSpec.getMode(spec);
    if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
        return View.MeasureSpec.makeMeasureSpec(
                View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
    }
    return spec;
}


private void parseAspectRatio(String aspect) {
    if (aspect != null) {
        int colonIndex = aspect.indexOf(':');
        if (colonIndex >= 0 && colonIndex < aspect.length() - 1) {
            String nominator = aspect.substring(0, colonIndex);
            String denominator = aspect.substring(colonIndex + 1);
            if (nominator.length() > 0 && denominator.length() > 0) {
                try {
                    float nominatorValue = Float.parseFloat(nominator);
                    float denominatorValue = Float.parseFloat(denominator);
                    if (nominatorValue > 0 && denominatorValue > 0) {
                        cellAspectRatio = Math.abs(nominatorValue / denominatorValue);
                        return;
                    }
                } catch (NumberFormatException e) {

                }
            }
        }
    }
    throw new IllegalArgumentException("Could not parse aspect ratio: '" + aspect + "'");
}

}

1 Ответ

0 голосов
/ 17 января 2019
Solution is this,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.NestedScrollView
    android:id="@+id/nestedScrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:overScrollMode="never">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
 </android.support.v4.widget.NestedScrollView>
</LinearLayout>

Now what kind of problem occurred when we used nestedScrollView and put recyclerView inside nestedScrollView, it scrolls in various speed depending on gesture. The scrolling feature will not be smooth.

So to fix this issue all you have to do after setting your adapter after setting your layout mannager
put this line ViewCompat.setNestedScrollingEnabled(recyclerView, false);
...