public class

Selection<K>

extends java.lang.Object

implements java.lang.Iterable<java.lang.Object>

 java.lang.Object

↳androidx.recyclerview.selection.Selection<K>

Subclasses:

MutableSelection<K>

Gradle dependencies

compile group: 'androidx.recyclerview', name: 'recyclerview-selection', version: '1.2.0-alpha01'

  • groupId: androidx.recyclerview
  • artifactId: recyclerview-selection
  • version: 1.2.0-alpha01

Artifact androidx.recyclerview:recyclerview-selection:1.2.0-alpha01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.recyclerview:recyclerview-selection com.android.support:recyclerview-selection

Overview

Object representing a "primary" selection and a "provisional" selection.

This class tracks selected items by managing two sets:

Primary Selection

Primary selection consists of items selected by a user. This represents the selection "at rest", as the selection does not contains items that are in a "provisional" selected state created by way of an ongoing gesture or band operation.

Provisional Selection

Provisional selections are selections which are interim in nature.

Provisional selection exists to address issues where a transitory selection might momentarily intersect with a previously established selection resulting in a some or all of the established selection being erased. Such situations may arise when band selection is being performed in "additive" mode (e.g. SHIFT or CTRL is pressed on the keyboard prior to mouse down), or when there's an active gesture selection (which can be initiated by long pressing an unselected item while there is an existing selection).

Summary

Methods
public booleancontains(java.lang.Object key)

public booleanequals(java.lang.Object other)

public inthashCode()

public booleanisEmpty()

public java.util.Iterator<java.lang.Object>iterator()

Returns an java.util.Iterator that iterators over the selection, *excluding* any provisional selection.

public intsize()

public java.lang.StringtoString()

from java.lang.Objectclone, finalize, getClass, notify, notifyAll, wait, wait, wait

Methods

public boolean contains(java.lang.Object key)

Returns:

true if the position is currently selected.

public java.util.Iterator<java.lang.Object> iterator()

Returns an java.util.Iterator that iterators over the selection, *excluding* any provisional selection.

public int size()

Returns:

size of the selection including both final and provisional selected items.

public boolean isEmpty()

Returns:

true if the selection is empty.

public java.lang.String toString()

public int hashCode()

public boolean equals(java.lang.Object other)

Source

/*
 * 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * Object representing a "primary" selection and a "provisional" selection.
 *
 * <p>
 * This class tracks selected items by managing two sets:
 *
 * <p>
 * <b>Primary Selection</b>
 *
 * <p>
 * Primary selection consists of items selected by a user. This represents the selection
 * "at rest", as the selection does not contains items that are in a "provisional" selected
 * state created by way of an ongoing gesture or band operation.
 *
 * <p>
 * <b>Provisional Selection</b>
 *
 * <p>
 * Provisional selections are selections which are interim in nature.
 *
 * <p>
 * Provisional selection exists to address issues where a transitory selection might
 * momentarily intersect with a previously established selection resulting in a some
 * or all of the established selection being erased. Such situations may arise
 * when band selection is being performed in "additive" mode (e.g. SHIFT or CTRL is pressed
 * on the keyboard prior to mouse down), or when there's an active gesture selection
 * (which can be initiated by long pressing an unselected item while there is an
 * existing selection).
 *
 * @param <K> Selection key type. @see {@link StorageStrategy} for supported types.
 * @see MutableSelection
 */
public class Selection<K> implements Iterable<K> {

    // NOTE: Not currently private as DefaultSelectionTracker directly manipulates values.
    final Set<K> mSelection;
    final Set<K> mProvisionalSelection;

    Selection() {
        mSelection = new LinkedHashSet<>();
        mProvisionalSelection = new LinkedHashSet<>();
    }

    /**
     * Used by {@link StorageStrategy} when restoring selection.
     */
    Selection(@NonNull Set<K> selection) {
        mSelection = selection;
        mProvisionalSelection = new LinkedHashSet<>();
    }

    /**
     * @return true if the position is currently selected.
     */
    public boolean contains(@Nullable K key) {
        return mSelection.contains(key) || mProvisionalSelection.contains(key);
    }

    /**
     * Returns an {@link Iterator} that iterators over the selection, *excluding*
     * any provisional selection.
     *
     * {@inheritDoc}
     */
    @Override
    public @NonNull Iterator<K> iterator() {
        return mSelection.iterator();
    }

    /**
     * @return size of the selection including both final and provisional selected items.
     */
    public int size() {
        return mSelection.size() + mProvisionalSelection.size();
    }

    /**
     * @return true if the selection is empty.
     */
    public boolean isEmpty() {
        return mSelection.isEmpty() && mProvisionalSelection.isEmpty();
    }

    /**
     * Sets the provisional selection, which is a temporary selection that can be saved,
     * canceled, or adjusted at a later time. When a new provision selection is applied, the old
     * one (if it exists) is abandoned.
     *
     * @return Map of ids added or removed. Added ids have a value of true, removed are false.
     */
    Map<K, Boolean> setProvisionalSelection(@NonNull Set<K> newSelection) {
        Map<K, Boolean> delta = new LinkedHashMap<>();

        for (K key : mProvisionalSelection) {
            // Mark each item that used to be in the provisional selection
            // but is not in the new provisional selection.
            if (!newSelection.contains(key) && !mSelection.contains(key)) {
                delta.put(key, false);
            }
        }

        for (K key : mSelection) {
            // Mark each item that in the selection but is not in the new
            // provisional selection.
            if (!newSelection.contains(key)) {
                delta.put(key, false);
            }
        }

        for (K key : newSelection) {
            // Mark each item that was not previously in the selection but is in the new
            // provisional selection.
            if (!mSelection.contains(key) && !mProvisionalSelection.contains(key)) {
                delta.put(key, true);
            }
        }

        // Now, iterate through the changes and actually add/remove them to/from the current
        // selection. This could not be done in the previous loops because changing the size of
        // the selection mid-iteration changes iteration order erroneously.
        for (Map.Entry<K, Boolean> entry : delta.entrySet()) {
            K key = entry.getKey();
            if (entry.getValue()) {
                mProvisionalSelection.add(key);
            } else {
                mProvisionalSelection.remove(key);
            }
        }

        return delta;
    }

    /**
     * Saves the existing provisional selection. Once the provisional selection is saved,
     * subsequent provisional selections which are different from this existing one cannot
     * cause items in this existing provisional selection to become deselected.
     */
    void mergeProvisionalSelection() {
        mSelection.addAll(mProvisionalSelection);
        mProvisionalSelection.clear();
    }

    /**
     * Abandons the existing provisional selection so that all items provisionally selected are
     * now deselected.
     */
    void clearProvisionalSelection() {
        mProvisionalSelection.clear();
    }

    /**
     * Adds a new item to the primary selection.
     *
     * @return true if the operation resulted in a modification to the selection.
     */
    boolean add(@NonNull K key) {
        return mSelection.add(key);
    }

    /**
     * Removes an item from the primary selection.
     *
     * @return true if the operation resulted in a modification to the selection.
     */
    boolean remove(@NonNull K key) {
        return mSelection.remove(key);
    }

    /**
     * Clears the primary selection. The provisional selection, if any, is unaffected.
     */
    void clear() {
        mSelection.clear();
    }

    /**
     * Clones primary and provisional selection from supplied {@link Selection}.
     * Does not copy active range data.
     */
    void copyFrom(@NonNull Selection<K> source) {
        mSelection.clear();
        mSelection.addAll(source.mSelection);

        mProvisionalSelection.clear();
        mProvisionalSelection.addAll(source.mProvisionalSelection);
    }

    @Override
    public String toString() {
        if (size() <= 0) {
            return "size=0, items=[]";
        }

        StringBuilder buffer = new StringBuilder(size() * 28);
        buffer.append("Selection{")
                .append("primary{size=" + mSelection.size())
                .append(", entries=" + mSelection)
                .append("}, provisional{size=" + mProvisionalSelection.size())
                .append(", entries=" + mProvisionalSelection)
                .append("}}");
        return buffer.toString();
    }

    @Override
    public int hashCode() {
        return mSelection.hashCode() ^ mProvisionalSelection.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        return (this == other)
                || (other instanceof Selection && isEqualTo((Selection<?>) other));
    }

    private boolean isEqualTo(Selection<?> other) {
        return mSelection.equals(other.mSelection)
                && mProvisionalSelection.equals(other.mProvisionalSelection);
    }
}