/* * Copyright (C) 2016 Haruki Hasegawa * * 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 com.h6ah4i.android.widget.advrecyclerview.composedadapter; import android.support.v7.widget.RecyclerView; import com.h6ah4i.android.widget.advrecyclerview.adapter.ItemViewTypeComposer; import java.util.Arrays; class SegmentedPositionTranslator { private static final int NO_CACHED_SEGMENT = 0; private static final int NO_CACHED_ITEM_COUNT = -1; private AdaptersSet mAdaptersSet; private int mLastOffsetCachedSegment; private int[] mSegmentItemCountCache; private int[] mSegmentOffsetCache; private int mCachedTotalItemCount; public SegmentedPositionTranslator(AdaptersSet adaptersSet) { mAdaptersSet = adaptersSet; mLastOffsetCachedSegment = NO_CACHED_SEGMENT; mCachedTotalItemCount = NO_CACHED_ITEM_COUNT; mSegmentItemCountCache = new int[ItemViewTypeComposer.MAX_SEGMENT + 1]; // NOTE: +1 room mSegmentOffsetCache = new int[ItemViewTypeComposer.MAX_SEGMENT + 1]; // NOTE: +1 room Arrays.fill(mSegmentItemCountCache, NO_CACHED_ITEM_COUNT); } public int getTotalItemCount() { if (mCachedTotalItemCount == NO_CACHED_ITEM_COUNT) { mCachedTotalItemCount = countTotalItems(); } return mCachedTotalItemCount; } public int getFlatPosition(int segment, int offset) { return getSegmentOffset(segment) + offset; } public long getSegmentedPosition(int flatPosition) { if (flatPosition == RecyclerView.NO_POSITION) { return RecyclerView.NO_POSITION; } final int binSearchResult = Arrays.binarySearch(mSegmentOffsetCache, 0, mLastOffsetCachedSegment, flatPosition); final int loopStartIndex; int segment; int localOffset; if (binSearchResult >= 0) { loopStartIndex = binSearchResult; segment = loopStartIndex; localOffset = 0; } else { loopStartIndex = Math.max(0, (~binSearchResult) - 1); segment = -1; localOffset = -1; } final int nSegments = mAdaptersSet.getSegmentCount(); int segmentOffset = mSegmentOffsetCache[loopStartIndex]; for (int i = loopStartIndex; i < nSegments; i++) { final int count = getSegmentItemCount(i); if ((segmentOffset + count) > flatPosition) { localOffset = flatPosition - segmentOffset; segment = i; break; } segmentOffset += count; } if (segment >= 0) { return AdaptersSet.composeSegmentedPosition(segment, localOffset); } else { return AdaptersSet.NO_SEGMENTED_POSITION; } } private int countTotalItems() { int segmentCount = mAdaptersSet.getSegmentCount(); if (segmentCount == 0) { return 0; } final int lastSegment = segmentCount - 1; final int lastSegmentOffset = getSegmentOffset(lastSegment); final int lastSegmentCount = getSegmentItemCount(lastSegment); return (lastSegmentOffset + lastSegmentCount); } public int getSegmentOffset(int segment) { if (segment <= mLastOffsetCachedSegment) { // cache hit return mSegmentOffsetCache[segment]; } else { // cache miss final int nSegments = mAdaptersSet.getSegmentCount(); final int loopStartIndex = mLastOffsetCachedSegment; int offset = mSegmentOffsetCache[loopStartIndex]; for (int i = loopStartIndex; i < segment; i++) { offset += getSegmentItemCount(i); } return offset; } } public int getSegmentItemCount(int segment) { if (mSegmentItemCountCache[segment] != NO_CACHED_ITEM_COUNT) { // cache hit return mSegmentItemCountCache[segment]; } else { // cache miss final int count = mAdaptersSet.getAdapter(segment).getItemCount(); mSegmentItemCountCache[segment] = count; if (segment == mLastOffsetCachedSegment) { mSegmentOffsetCache[segment + 1] = mSegmentOffsetCache[segment] + count; mLastOffsetCachedSegment = segment + 1; } return count; } } public void invalidateSegment(int segment) { mCachedTotalItemCount = NO_CACHED_ITEM_COUNT; mLastOffsetCachedSegment = Math.min(mLastOffsetCachedSegment, segment); mSegmentItemCountCache[segment] = NO_CACHED_ITEM_COUNT; } public void invalidateAll() { mCachedTotalItemCount = NO_CACHED_ITEM_COUNT; mLastOffsetCachedSegment = NO_CACHED_SEGMENT; Arrays.fill(mSegmentItemCountCache, NO_CACHED_ITEM_COUNT); } public void release() { mAdaptersSet = null; mSegmentItemCountCache = null; mSegmentOffsetCache = null; } }