TwoWayView布局管理器的反向布局

时间:2015-03-04 11:10:21

标签: android android-recyclerview

有没有人有一个简单的解决方法来反转TwoWayView布局管理器的布局子项?

/*
 * Copyright (C) 2014 Lucas Rocha
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.lucasr.twowayview.widget;

import android.graphics.Rect;

import org.lucasr.twowayview.TwoWayLayoutManager.Direction;
import org.lucasr.twowayview.TwoWayLayoutManager.Orientation;

class Lanes {
    public static final int NO_LANE = -1;

    private final BaseLayoutManager mLayout;
    private final boolean mIsVertical;
    private final Rect[] mLanes;
    private final Rect[] mSavedLanes;
    private final int mLaneSize;

    private final Rect mTempRect = new Rect();
    private final LaneInfo mTempLaneInfo = new LaneInfo();

    private Integer mInnerStart;
    private Integer mInnerEnd;

    public static class LaneInfo {
        public int startLane;
        public int anchorLane;

        public boolean isUndefined() {
            return (startLane == NO_LANE || anchorLane == NO_LANE);
        }

        public void set(int startLane, int anchorLane) {
            this.startLane = startLane;
            this.anchorLane = anchorLane;
        }

        public void setUndefined() {
            startLane = NO_LANE;
            anchorLane = NO_LANE;
        }
    }

    public Lanes(BaseLayoutManager layout, Orientation orientation, Rect[] lanes, int laneSize) {
        mLayout = layout;
        mIsVertical = (orientation == Orientation.VERTICAL);
        mLanes = lanes;
        mLaneSize = laneSize;

        mSavedLanes = new Rect[mLanes.length];
        for (int i = 0; i < mLanes.length; i++) {
            mSavedLanes[i] = new Rect();
        }
    }

    public Lanes(BaseLayoutManager layout, int laneCount) {
        mLayout = layout;
        mIsVertical = layout.isVertical();

        mLanes = new Rect[laneCount];
        mSavedLanes = new Rect[laneCount];
        for (int i = 0; i < laneCount; i++) {
            mLanes[i] = new Rect();
            mSavedLanes[i] = new Rect();
        }

        mLaneSize = calculateLaneSize(layout, laneCount);

        final int paddingLeft = layout.getPaddingLeft();
        final int paddingTop = layout.getPaddingTop();

        for (int i = 0; i < laneCount; i++) {
            final int laneStart = i * mLaneSize;

            final int l = paddingLeft + (mIsVertical ? laneStart : 0);
            final int t = paddingTop + (mIsVertical ? 0 : laneStart);
            final int r = (mIsVertical ? l + mLaneSize : l);
            final int b = (mIsVertical ? t : t + mLaneSize);

            mLanes[i].set(l, t, r, b);
        }
    }

    public static int calculateLaneSize(BaseLayoutManager layout, int laneCount) {
        if (layout.isVertical()) {
            final int paddingLeft = layout.getPaddingLeft();
            final int paddingRight = layout.getPaddingRight();
            final int width = layout.getWidth() - paddingLeft - paddingRight;
            return width / laneCount;
        } else {
            final int paddingTop = layout.getPaddingTop();
            final int paddingBottom = layout.getPaddingBottom();
            final int height = layout.getHeight() - paddingTop - paddingBottom;
            return height / laneCount;
        }
    }

    private void invalidateEdges() {
        mInnerStart = null;
        mInnerEnd = null;
    }

    public Orientation getOrientation() {
        return (mIsVertical ? Orientation.VERTICAL : Orientation.HORIZONTAL);
    }

    public void save() {
        for (int i = 0; i < mLanes.length; i++) {
            mSavedLanes[i].set(mLanes[i]);
        }
    }

    public void restore() {
        for (int i = 0; i < mLanes.length; i++) {
            mLanes[i].set(mSavedLanes[i]);
        }
    }

    public int getLaneSize() {
        return mLaneSize;
    }

    public int getCount() {
        return mLanes.length;
    }

    private void offsetLane(int lane, int offset) {
        mLanes[lane].offset(mIsVertical ? 0 : offset,
                mIsVertical ? offset : 0);
    }

    public void offset(int offset) {
        for (int i = 0; i < mLanes.length; i++) {
            offset(i, offset);
        }

        invalidateEdges();
    }

    public void offset(int lane, int offset) {
        offsetLane(lane, offset);
        invalidateEdges();
    }

    public void getLane(int lane, Rect laneRect) {
        laneRect.set(mLanes[lane]);
    }

    public int pushChildFrame(Rect outRect, int lane, int margin, Direction direction) {
        final int delta;

        final Rect laneRect = mLanes[lane];
        if (mIsVertical) {
            if (direction == Direction.END) {
                delta = outRect.top - laneRect.bottom;
                laneRect.bottom = outRect.bottom + margin;
            } else {
                delta = outRect.bottom - laneRect.top;
                laneRect.top = outRect.top - margin;
            }
        } else {
            if (direction == Direction.END) {
                delta = outRect.left - laneRect.right;
                laneRect.right = outRect.right + margin;
            } else {
                delta = outRect.right - laneRect.left;
                laneRect.left = outRect.left - margin;
            }
        }

        invalidateEdges();

        return delta;
    }

    public void popChildFrame(Rect outRect, int lane, int margin, Direction direction) {
        final Rect laneRect = mLanes[lane];
        if (mIsVertical) {
            if (direction == Direction.END) {
                laneRect.top = outRect.bottom - margin;
            } else {
                laneRect.bottom = outRect.top + margin;
            }
        } else {
            if (direction == Direction.END) {
                laneRect.left = outRect.right - margin;
            } else {
                laneRect.right = outRect.left + margin;
            }
        }

        invalidateEdges();
    }

    public void getChildFrame(Rect outRect, int childWidth, int childHeight, LaneInfo laneInfo,
                              Direction direction) {
        final Rect startRect = mLanes[laneInfo.startLane];

        // The anchor lane only applies when we're get child frame in the direction
        // of the forward scroll. We'll need to rethink this once we start working on
        // RTL support.
        final int anchorLane =
                (direction == Direction.END ? laneInfo.anchorLane : laneInfo.startLane);
        final Rect anchorRect = mLanes[anchorLane];

        if (mIsVertical) {
            outRect.left = startRect.left;
            outRect.top =
                    (direction == Direction.END ? anchorRect.bottom : anchorRect.top - childHeight);
        } else {
            outRect.top = startRect.top;
            outRect.left =
                    (direction == Direction.END ? anchorRect.right : anchorRect.left - childWidth);
        }

        outRect.right = outRect.left + childWidth;
        outRect.bottom = outRect.top + childHeight;
    }

    private boolean intersects(int start, int count, Rect r) {
        for (int l = start; l < start + count; l++) {
            if (Rect.intersects(mLanes[l], r)) {
                return true;
            }
        }

        return false;
    }

    private int findLaneThatFitsSpan(int anchorLane, int laneSpan, Direction direction) {
        final int findStart = Math.max(0, anchorLane - laneSpan + 1);
        final int findEnd = Math.min(findStart + laneSpan, mLanes.length - laneSpan + 1);
        for (int l = findStart; l < findEnd; l++) {
            mTempLaneInfo.set(l, anchorLane);

            getChildFrame(mTempRect, mIsVertical ? laneSpan * mLaneSize : 1,
                    mIsVertical ? 1 : laneSpan * mLaneSize, mTempLaneInfo, direction);

            if (!intersects(l, laneSpan, mTempRect)) {
                return l;
            }
        }

        return Lanes.NO_LANE;
    }

    public void findLane(LaneInfo outInfo, int laneSpan, Direction direction) {
        outInfo.setUndefined();

        int targetEdge = (direction == Direction.END ? Integer.MAX_VALUE : Integer.MIN_VALUE);
        for (int l = 0; l < mLanes.length; l++) {
            final int laneEdge;
            if (mIsVertical) {
                laneEdge = (direction == Direction.END ? mLanes[l].bottom : mLanes[l].top);
            } else {
                laneEdge = (direction == Direction.END ? mLanes[l].right : mLanes[l].left);
            }

            if ((direction == Direction.END && laneEdge < targetEdge) ||
                (direction == Direction.START && laneEdge > targetEdge)) {

                final int targetLane = findLaneThatFitsSpan(l, laneSpan, direction);
                if (targetLane != NO_LANE) {
                    targetEdge = laneEdge;
                    outInfo.set(targetLane, l);
                }
            }
        }
    }

    public void reset(Direction direction) {
        for (int i = 0; i < mLanes.length; i++) {
            final Rect laneRect = mLanes[i];
            if (mIsVertical) {
                if (direction == Direction.START) {
                    laneRect.bottom = laneRect.top;
                } else {
                    laneRect.top = laneRect.bottom;
                }
            } else {
                if (direction == Direction.START) {
                    laneRect.right = laneRect.left;
                } else {
                    laneRect.left = laneRect.right;
                }
            }
        }

        invalidateEdges();
    }

    public void reset(int offset) {
        for (int i = 0; i < mLanes.length; i++) {
            final Rect laneRect = mLanes[i];

            laneRect.offsetTo(mIsVertical ? laneRect.left : offset,
                              mIsVertical ? offset : laneRect.top);

            if (mIsVertical) {
                laneRect.bottom = laneRect.top;
            } else {
                laneRect.right = laneRect.left;
            }
        }

        invalidateEdges();
    }

    public int getInnerStart() {
        if (mInnerStart != null) {
            return mInnerStart;
        }

        mInnerStart = Integer.MIN_VALUE;
        for (int i = 0; i < mLanes.length; i++) {
            final Rect laneRect = mLanes[i];
            mInnerStart = Math.max(mInnerStart, mIsVertical ? laneRect.top : laneRect.left);
        }

        return mInnerStart;
    }

    public int getInnerEnd() {
        if (mInnerEnd != null) {
            return mInnerEnd;
        }

        mInnerEnd = Integer.MAX_VALUE;
        for (int i = 0; i < mLanes.length; i++) {
            final Rect laneRect = mLanes[i];
            mInnerEnd = Math.min(mInnerEnd, mIsVertical ? laneRect.bottom : laneRect.right);
        }

        return mInnerEnd;
    }
}

有趣的部分应该是:

public void getChildFrame(Rect outRect, int childWidth, int childHeight, LaneInfo laneInfo,
                          TwoWayLayoutManager.Direction direction) {
    final Rect startRect = mLanes[laneInfo.startLane];

    // The anchor lane only applies when we're get child frame in the direction
    // of the forward scroll. We'll need to rethink this once we start working on
    // RTL support.
    final int anchorLane =
            (direction == TwoWayLayoutManager.Direction.END ? laneInfo.anchorLane : laneInfo.startLane);
    final Rect anchorRect = mLanes[anchorLane];

    if (mIsVertical) {
        outRect.left = startRect.left;
        outRect.top =
                (direction == TwoWayLayoutManager.Direction.END ? anchorRect.bottom : anchorRect.top - childHeight);
    } else {
        outRect.top = startRect.top;
        outRect.left =
                (direction == TwoWayLayoutManager.Direction.END ? anchorRect.right : anchorRect.left - childWidth);
    }

    outRect.right = outRect.left + childWidth;
    outRect.bottom = outRect.top + childHeight;
}

其他参考资料:

谢谢!

0 个答案:

没有答案