DefaultBandHost.java

/*
 * Copyright 2017 The Android Open Source Project
 *
 * 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 androidx.recyclerview.selection;

import static androidx.core.util.Preconditions.checkArgument;

import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.recyclerview.selection.SelectionTracker.SelectionPredicate;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;

/**
 * RecyclerView backed {@link BandSelectionHelper.BandHost}.
 */
final class DefaultBandHost<K> extends GridModel.GridHost<K> {

    private static final Rect NILL_RECT = new Rect(0, 0, 0, 0);

    private final RecyclerView mRecyclerView;
    private final Drawable mBand;
    private final ItemKeyProvider<K> mKeyProvider;
    private final SelectionPredicate<K> mSelectionPredicate;

    DefaultBandHost(
            @NonNull RecyclerView recyclerView,
            @DrawableRes int bandOverlayId,
            @NonNull ItemKeyProvider<K> keyProvider,
            @NonNull SelectionPredicate<K> selectionPredicate) {

        checkArgument(recyclerView != null);

        mRecyclerView = recyclerView;
        mBand = mRecyclerView.getContext().getResources().getDrawable(bandOverlayId);

        checkArgument(mBand != null);
        checkArgument(keyProvider != null);
        checkArgument(selectionPredicate != null);

        mKeyProvider = keyProvider;
        mSelectionPredicate = selectionPredicate;

        mRecyclerView.addItemDecoration(
                new ItemDecoration() {
                    @Override
                    public void onDrawOver(
                            Canvas canvas,
                            RecyclerView unusedParent,
                            RecyclerView.State unusedState) {
                        DefaultBandHost.this.onDrawBand(canvas);
                    }
                });
    }

    @Override
    GridModel<K> createGridModel() {
        return new GridModel<>(this, mKeyProvider, mSelectionPredicate);
    }

    @Override
    int getAdapterPositionAt(int index) {
        return mRecyclerView.getChildAdapterPosition(mRecyclerView.getChildAt(index));
    }

    @Override
    void addOnScrollListener(@NonNull OnScrollListener listener) {
        mRecyclerView.addOnScrollListener(listener);
    }

    @Override
    void removeOnScrollListener(@NonNull OnScrollListener listener) {
        mRecyclerView.removeOnScrollListener(listener);
    }

    @Override
    Point createAbsolutePoint(@NonNull Point relativePoint) {
        return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(),
                relativePoint.y + mRecyclerView.computeVerticalScrollOffset());
    }

    @Override
    Rect getAbsoluteRectForChildViewAt(int index) {
        final View child = mRecyclerView.getChildAt(index);
        final Rect childRect = new Rect();
        child.getHitRect(childRect);
        childRect.left += mRecyclerView.computeHorizontalScrollOffset();
        childRect.right += mRecyclerView.computeHorizontalScrollOffset();
        childRect.top += mRecyclerView.computeVerticalScrollOffset();
        childRect.bottom += mRecyclerView.computeVerticalScrollOffset();
        return childRect;
    }

    @Override
    int getVisibleChildCount() {
        return mRecyclerView.getChildCount();
    }

    @Override
    int getColumnCount() {
        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            return ((GridLayoutManager) layoutManager).getSpanCount();
        }

        // Otherwise, it is a list with 1 column.
        return 1;
    }

    @Override
    void showBand(@NonNull Rect rect) {
        mBand.setBounds(rect);
        // TODO: mRecyclerView.invalidateItemDecorations() should work, but it isn't currently.
        // NOTE: That without invalidating rv, the band only gets updated
        // when the pointer moves off a the item view into "NO_POSITION" territory.
        mRecyclerView.invalidate();
    }

    @Override
    void hideBand() {
        mBand.setBounds(NILL_RECT);
        // TODO: mRecyclerView.invalidateItemDecorations() should work, but it isn't currently.
        mRecyclerView.invalidate();
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void onDrawBand(@NonNull Canvas c) {
        mBand.draw(c);
    }

    @Override
    boolean hasView(int pos) {
        return mRecyclerView.findViewHolderForAdapterPosition(pos) != null;
    }
}