public class

WearableRecyclerView

extends RecyclerView

 java.lang.Object

↳ViewGroup

androidx.recyclerview.widget.RecyclerView

↳androidx.wear.widget.WearableRecyclerView

Gradle dependencies

compile group: 'androidx.wear', name: 'wear', version: '1.3.0-alpha02'

  • groupId: androidx.wear
  • artifactId: wear
  • version: 1.3.0-alpha02

Artifact androidx.wear:wear:1.3.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.wear:wear com.android.support:wear

Androidx class mapping:

androidx.wear.widget.WearableRecyclerView android.support.wear.widget.WearableRecyclerView

Overview

Wearable specific implementation of the RecyclerView enabling WearableRecyclerView.setCircularScrollingGestureEnabled(boolean) circular scrolling} and semi-circular layouts.

Summary

Fields
from RecyclerViewHORIZONTAL, INVALID_TYPE, NO_ID, NO_POSITION, SCROLL_STATE_DRAGGING, SCROLL_STATE_IDLE, SCROLL_STATE_SETTLING, TOUCH_SLOP_DEFAULT, TOUCH_SLOP_PAGING, UNDEFINED_DURATION, VERTICAL
Constructors
publicWearableRecyclerView(Context context)

publicWearableRecyclerView(Context context, AttributeSet attrs)

publicWearableRecyclerView(Context context, AttributeSet attrs, int defStyle)

publicWearableRecyclerView(Context context, AttributeSet attrs, int defStyle, int defStyleRes)

Methods
public floatgetBezelFraction()

Returns the current bezel width for circular scrolling as a fraction of the screen's radius.

public floatgetScrollDegreesPerScreen()

Returns how many degrees does the user have to rotate for to scroll through one screen height.

public booleanisCircularScrollingGestureEnabled()

Returns whether circular scrolling is enabled for this view.

public booleanisEdgeItemsCenteringEnabled()

Returns whether the view is currently configured to center the edge children.

protected voidonAttachedToWindow()

protected voidonDetachedFromWindow()

public booleanonTouchEvent(MotionEvent event)

public voidsetBezelFraction(float fraction)

Taps within this radius and the radius of the screen are considered close enough to the bezel to be candidates for circular scrolling.

public voidsetCircularScrollingGestureEnabled(boolean circularScrollingGestureEnabled)

Enables/disables circular touch scrolling for this view.

public voidsetEdgeItemsCenteringEnabled(boolean isEnabled)

Use this method to configure the WearableRecyclerView on round watches to always align the first and last items with the vertical center of the screen.

public voidsetScrollDegreesPerScreen(float degreesPerScreen)

Sets how many degrees the user has to rotate by to scroll through one screen height when they are using the circular scrolling gesture.The default value equates 180 degrees scroll to one screen.

from RecyclerViewaddFocusables, addItemDecoration, addItemDecoration, addOnChildAttachStateChangeListener, addOnItemTouchListener, addOnScrollListener, addRecyclerListener, checkLayoutParams, clearOnChildAttachStateChangeListeners, clearOnScrollListeners, computeHorizontalScrollExtent, computeHorizontalScrollOffset, computeHorizontalScrollRange, computeVerticalScrollExtent, computeVerticalScrollOffset, computeVerticalScrollRange, dispatchNestedFling, dispatchNestedPreFling, dispatchNestedPreScroll, dispatchNestedPreScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchNestedScroll, dispatchPopulateAccessibilityEvent, dispatchRestoreInstanceState, dispatchSaveInstanceState, draw, drawChild, findChildViewUnder, findContainingItemView, findContainingViewHolder, findViewHolderForAdapterPosition, findViewHolderForItemId, findViewHolderForLayoutPosition, findViewHolderForPosition, fling, focusSearch, generateDefaultLayoutParams, generateLayoutParams, generateLayoutParams, getAccessibilityClassName, getAdapter, getBaseline, getChildAdapterPosition, getChildDrawingOrder, getChildItemId, getChildLayoutPosition, getChildPosition, getChildViewHolder, getClipToPadding, getCompatAccessibilityDelegate, getDecoratedBoundsWithMargins, getEdgeEffectFactory, getItemAnimator, getItemDecorationAt, getItemDecorationCount, getLayoutManager, getMaxFlingVelocity, getMinFlingVelocity, getOnFlingListener, getPreserveFocusAfterLayout, getRecycledViewPool, getScrollState, hasFixedSize, hasNestedScrollingParent, hasNestedScrollingParent, hasPendingAdapterUpdates, invalidateItemDecorations, isAnimating, isAttachedToWindow, isComputingLayout, isLayoutFrozen, isLayoutSuppressed, isNestedScrollingEnabled, nestedScrollBy, offsetChildrenHorizontal, offsetChildrenVertical, onChildAttachedToWindow, onChildDetachedFromWindow, onDraw, onGenericMotionEvent, onInterceptTouchEvent, onLayout, onMeasure, onRequestFocusInDescendants, onRestoreInstanceState, onSaveInstanceState, onScrolled, onScrollStateChanged, onSizeChanged, removeDetachedView, removeItemDecoration, removeItemDecorationAt, removeOnChildAttachStateChangeListener, removeOnItemTouchListener, removeOnScrollListener, removeRecyclerListener, requestChildFocus, requestChildRectangleOnScreen, requestDisallowInterceptTouchEvent, requestLayout, scrollBy, scrollTo, scrollToPosition, sendAccessibilityEventUnchecked, setAccessibilityDelegateCompat, setAdapter, setChildDrawingOrderCallback, setClipToPadding, setEdgeEffectFactory, setHasFixedSize, setItemAnimator, setItemViewCacheSize, setLayoutFrozen, setLayoutManager, setLayoutTransition, setNestedScrollingEnabled, setOnFlingListener, setOnScrollListener, setPreserveFocusAfterLayout, setRecycledViewPool, setRecyclerListener, setScrollingTouchSlop, setViewCacheExtension, smoothScrollBy, smoothScrollBy, smoothScrollBy, smoothScrollToPosition, startNestedScroll, startNestedScroll, stopNestedScroll, stopNestedScroll, stopScroll, suppressLayout, swapAdapter
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public WearableRecyclerView(Context context)

public WearableRecyclerView(Context context, AttributeSet attrs)

public WearableRecyclerView(Context context, AttributeSet attrs, int defStyle)

public WearableRecyclerView(Context context, AttributeSet attrs, int defStyle, int defStyleRes)

Methods

public boolean onTouchEvent(MotionEvent event)

protected void onAttachedToWindow()

protected void onDetachedFromWindow()

public void setCircularScrollingGestureEnabled(boolean circularScrollingGestureEnabled)

Enables/disables circular touch scrolling for this view. When enabled, circular touch gestures around the edge of the screen will cause the view to scroll up or down. Related methods let you specify the characteristics of the scrolling, like the speed of the scroll or the are considered for the start of this scrolling gesture.

See also: WearableRecyclerView.setScrollDegreesPerScreen(float), WearableRecyclerView.setBezelFraction(float)

public boolean isCircularScrollingGestureEnabled()

Returns whether circular scrolling is enabled for this view.

See also: WearableRecyclerView.setCircularScrollingGestureEnabled(boolean)

public void setScrollDegreesPerScreen(float degreesPerScreen)

Sets how many degrees the user has to rotate by to scroll through one screen height when they are using the circular scrolling gesture.The default value equates 180 degrees scroll to one screen.

Parameters:

degreesPerScreen: the number of degrees to rotate by to scroll through one whole height of the screen,

See also: WearableRecyclerView.setCircularScrollingGestureEnabled(boolean)

public float getScrollDegreesPerScreen()

Returns how many degrees does the user have to rotate for to scroll through one screen height.

See also: WearableRecyclerView.setCircularScrollingGestureEnabled(boolean), WearableRecyclerView.setScrollDegreesPerScreen(float)

public void setBezelFraction(float fraction)

Taps within this radius and the radius of the screen are considered close enough to the bezel to be candidates for circular scrolling. Expressed as a fraction of the screen's radius. The default is the whole screen i.e 1.0f.

public float getBezelFraction()

Returns the current bezel width for circular scrolling as a fraction of the screen's radius.

See also: WearableRecyclerView.setBezelFraction(float)

public void setEdgeItemsCenteringEnabled(boolean isEnabled)

Use this method to configure the WearableRecyclerView on round watches to always align the first and last items with the vertical center of the screen. This effectively moves the start and end of the list to the middle of the screen if the user has scrolled so far. It takes the height of the children into account so that they are correctly centered. On nonRound watches, this method has no effect and original padding is used.

Parameters:

isEnabled: set to true if you wish to align the edge children (first and last) with the center of the screen.

public boolean isEdgeItemsCenteringEnabled()

Returns whether the view is currently configured to center the edge children. See WearableRecyclerView.setEdgeItemsCenteringEnabled(boolean) for details.

Source

/*
 * Copyright (C) 2016 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.wear.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;

import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.wear.R;

/**
 * Wearable specific implementation of the {@link RecyclerView} enabling {@link
 * #setCircularScrollingGestureEnabled(boolean)} circular scrolling} and semi-circular layouts.
 *
 * @see #setCircularScrollingGestureEnabled(boolean)
 */
public class WearableRecyclerView extends RecyclerView {
    private static final String TAG = "WearableRecyclerView";

    private static final int NO_VALUE = Integer.MIN_VALUE;

    private final ScrollManager mScrollManager = new ScrollManager();
    private boolean mCircularScrollingEnabled;
    private boolean mEdgeItemsCenteringEnabled;
    boolean mCenterEdgeItemsWhenThereAreChildren;

    private int mOriginalPaddingTop = NO_VALUE;
    private int mOriginalPaddingBottom = NO_VALUE;

    /** Pre-draw listener which is used to adjust the padding on this view before its first draw. */
    private final ViewTreeObserver.OnPreDrawListener mPaddingPreDrawListener =
            new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    if (mCenterEdgeItemsWhenThereAreChildren && getChildCount() > 0) {
                        setupCenteredPadding();
                        mCenterEdgeItemsWhenThereAreChildren = false;
                    }
                    return true;
                }
            };

    public WearableRecyclerView(Context context) {
        this(context, null);
    }

    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        this(context, attrs, defStyle, 0);
    }

    public WearableRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle,
            int defStyleRes) {
        super(context, attrs, defStyle);

        setHasFixedSize(true);
        // Padding is used to center the top and bottom items in the list, don't clip to padding to
        // allows the items to draw in that space.
        setClipToPadding(false);

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WearableRecyclerView,
                    defStyle, defStyleRes);
            ViewCompat.saveAttributeDataForStyleable(
                    this, context, R.styleable.WearableRecyclerView, attrs, a, defStyle,
                    defStyleRes);

            setCircularScrollingGestureEnabled(
                    a.getBoolean(
                            R.styleable.WearableRecyclerView_circularScrollingGestureEnabled,
                            mCircularScrollingEnabled));
            setBezelFraction(
                    a.getFloat(R.styleable.WearableRecyclerView_bezelWidth,
                            mScrollManager.getBezelWidth()));
            setScrollDegreesPerScreen(
                    a.getFloat(
                            R.styleable.WearableRecyclerView_scrollDegreesPerScreen,
                            mScrollManager.getScrollDegreesPerScreen()));
            a.recycle();
        }
    }

    void setupCenteredPadding() {
        if (getChildCount() < 1 || !mEdgeItemsCenteringEnabled) {
            return;
        }
        // All the children in the view are the same size, as we set setHasFixedSize
        // to true, so the height of the first child is the same as all of them.
        View child = getChildAt(0);
        int height = child.getHeight();
        // This is enough padding to center the child view in the parent.
        int desiredPadding = (int) ((getHeight() * 0.5f) - (height * 0.5f));

        if (getPaddingTop() != desiredPadding) {
            mOriginalPaddingTop = getPaddingTop();
            mOriginalPaddingBottom = getPaddingBottom();
            // The view is symmetric along the vertical axis, so the top and bottom
            // can be the same.
            setPadding(getPaddingLeft(), desiredPadding, getPaddingRight(), desiredPadding);

            // The focused child should be in the center, so force a scroll to it.
            View focusedChild = getFocusedChild();
            int focusedPosition =
                    (focusedChild != null) ? getLayoutManager().getPosition(
                            focusedChild) : 0;
            getLayoutManager().scrollToPosition(focusedPosition);
        }
    }

    private void setupOriginalPadding() {
        if (mOriginalPaddingTop == NO_VALUE) {
            return;
        } else {
            setPadding(getPaddingLeft(), mOriginalPaddingTop, getPaddingRight(),
                    mOriginalPaddingBottom);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mCircularScrollingEnabled && mScrollManager.onTouchEvent(event)) {
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    @SuppressWarnings("deprecation") /* Display.getSize() */
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Point screenSize = new Point();
        getDisplay().getSize(screenSize);
        mScrollManager.setRecyclerView(this, screenSize.x, screenSize.y);
        getViewTreeObserver().addOnPreDrawListener(mPaddingPreDrawListener);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mScrollManager.clearRecyclerView();
        getViewTreeObserver().removeOnPreDrawListener(mPaddingPreDrawListener);
    }

    /**
     * Enables/disables circular touch scrolling for this view. When enabled, circular touch
     * gestures around the edge of the screen will cause the view to scroll up or down. Related
     * methods let you specify the characteristics of the scrolling, like the speed of the scroll
     * or the are considered for the start of this scrolling gesture.
     *
     * @see #setScrollDegreesPerScreen(float)
     * @see #setBezelFraction(float)
     */
    public void setCircularScrollingGestureEnabled(boolean circularScrollingGestureEnabled) {
        mCircularScrollingEnabled = circularScrollingGestureEnabled;
    }

    /**
     * Returns whether circular scrolling is enabled for this view.
     *
     * @see #setCircularScrollingGestureEnabled(boolean)
     */
    public boolean isCircularScrollingGestureEnabled() {
        return mCircularScrollingEnabled;
    }

    /**
     * Sets how many degrees the user has to rotate by to scroll through one screen height when they
     * are using the circular scrolling gesture.The default value equates 180 degrees scroll to one
     * screen.
     *
     * @see #setCircularScrollingGestureEnabled(boolean)
     *
     * @param degreesPerScreen the number of degrees to rotate by to scroll through one whole
     *                         height of the screen,
     */
    public void setScrollDegreesPerScreen(float degreesPerScreen) {
        mScrollManager.setScrollDegreesPerScreen(degreesPerScreen);
    }

    /**
     * Returns how many degrees does the user have to rotate for to scroll through one screen
     * height.
     *
     * @see #setCircularScrollingGestureEnabled(boolean)
     * @see #setScrollDegreesPerScreen(float).
     */
    public float getScrollDegreesPerScreen() {
        return mScrollManager.getScrollDegreesPerScreen();
    }

    /**
     * Taps within this radius and the radius of the screen are considered close enough to the
     * bezel to be candidates for circular scrolling. Expressed as a fraction of the screen's
     * radius. The default is the whole screen i.e 1.0f.
     */
    public void setBezelFraction(float fraction) {
        mScrollManager.setBezelWidth(fraction);
    }

    /**
     * Returns the current bezel width for circular scrolling as a fraction of the screen's
     * radius.
     *
     * @see #setBezelFraction(float)
     */
    public float getBezelFraction() {
        return mScrollManager.getBezelWidth();
    }

    /**
     * Use this method to configure the {@link WearableRecyclerView} on round watches to always
     * align the first and last items with the vertical center of the screen. This effectively moves
     * the start and end of the list to the middle of the screen if the user has scrolled so far.
     * It takes the height of the children into account so that they are correctly centered.
     * On nonRound watches, this method has no effect and original padding is used.
     *
     * @param isEnabled set to true if you wish to align the edge children (first and last)
     *                        with the center of the screen.
     */
    public void setEdgeItemsCenteringEnabled(boolean isEnabled) {
        if (!getResources().getConfiguration().isScreenRound()) {
            mEdgeItemsCenteringEnabled = false;
            return;
        }
        mEdgeItemsCenteringEnabled = isEnabled;
        if (mEdgeItemsCenteringEnabled) {
            if (getChildCount() > 0) {
                setupCenteredPadding();
            } else {
                mCenterEdgeItemsWhenThereAreChildren = true;
            }
        } else {
            setupOriginalPadding();
            mCenterEdgeItemsWhenThereAreChildren = false;
        }
    }

    /**
     * Returns whether the view is currently configured to center the edge children. See {@link
     * #setEdgeItemsCenteringEnabled} for details.
     */
    public boolean isEdgeItemsCenteringEnabled() {
        return mEdgeItemsCenteringEnabled;
    }
}