public class

RecyclerViewParallax

extends Parallax<RecyclerViewParallax.ChildPositionProperty>

 java.lang.Object

androidx.leanback.widget.Parallax<RecyclerViewParallax.ChildPositionProperty>

↳androidx.leanback.widget.RecyclerViewParallax

Subclasses:

DetailsParallax

Gradle dependencies

compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha04'

  • groupId: androidx.leanback
  • artifactId: leanback
  • version: 1.2.0-alpha04

Artifact androidx.leanback:leanback:1.2.0-alpha04 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.leanback:leanback com.android.support:leanback-v17

Androidx class mapping:

androidx.leanback.widget.RecyclerViewParallax android.support.v17.leanback.widget.RecyclerViewParallax

Overview

Implementation of Parallax class for RecyclerView. This class allows users to track position of specific views inside RecyclerView relative to itself. See RecyclerViewParallax.ChildPositionProperty for details.

Summary

Constructors
publicRecyclerViewParallax()

Methods
public abstract android.util.PropertycreateProperty(java.lang.String name, int index)

Create a new Property object.

public abstract floatgetMaxValue()

Return the max value which is typically size of parent visible area, e.g.

public RecyclerViewgetRecyclerView()

public voidsetRecyclerView(RecyclerView recyclerView)

Set RecyclerView that this Parallax will register onScrollListener.

public voidupdateValues()

Manually update values.

from Parallax<PropertyT>addEffect, addProperty, getEffects, getProperties, removeAllEffects, removeEffect
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public RecyclerViewParallax()

Methods

public abstract android.util.Property createProperty(java.lang.String name, int index)

Create a new Property object. App does not directly call this method. See Parallax.addProperty(String).

Parameters:

index: Index of the property in this Parallax object.

Returns:

Newly created Property object.

public abstract float getMaxValue()

Return the max value which is typically size of parent visible area, e.g. RecyclerView's height if we are tracking Y position of a child. The size can be used to calculate marker value using the provided fraction of FloatPropertyMarkerValue.

Returns:

Size of parent visible area.

public void setRecyclerView(RecyclerView recyclerView)

Set RecyclerView that this Parallax will register onScrollListener.

Parameters:

recyclerView: RecyclerView to register onScrollListener.

public void updateValues()

Manually update values. This is used for changes not controlled by RecyclerView. E.g. called by a Slide transition that changes translation of the view.

public RecyclerView getRecyclerView()

Returns:

Currently RecylerView that the source has registered onScrollListener.

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.leanback.widget;

import static androidx.recyclerview.widget.RecyclerView.LayoutManager;
import static androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import static androidx.recyclerview.widget.RecyclerView.ViewHolder;

import android.graphics.Rect;
import android.util.Property;
import android.view.View;

import androidx.recyclerview.widget.RecyclerView;

/**
 * Implementation of {@link Parallax} class for {@link RecyclerView}. This class
 * allows users to track position of specific views inside {@link RecyclerView} relative to
 * itself. See {@link ChildPositionProperty} for details.
 */
public class RecyclerViewParallax extends Parallax<RecyclerViewParallax.ChildPositionProperty> {
    RecyclerView mRecylerView;
    boolean mIsVertical;

    OnScrollListener mOnScrollListener = new OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            updateValues();
        }
    };

    View.OnLayoutChangeListener mOnLayoutChangeListener = new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View view, int l, int t, int r, int b,
                int oldL, int oldT, int oldR, int oldB) {
            updateValues();
        }
    };

    /**
     * Subclass of {@link Parallax.IntProperty}. Using this Property, users can track a
     * RecylerView child's position inside recyclerview. i.e.
     *
     * tracking_pos = view.top + fraction * view.height() + offset
     *
     * This way we can track top using fraction 0 and bottom using fraction 1.
     */
    public static final class ChildPositionProperty extends Parallax.IntProperty {
        int mAdapterPosition;
        int mViewId;
        int mOffset;
        float mFraction;

        ChildPositionProperty(String name, int index) {
            super(name, index);
        }

        /**
         * Sets adapter position of the recyclerview child to track.
         *
         * @param adapterPosition Zero based position in adapter.
         * @return This ChildPositionProperty object.
         */
        public ChildPositionProperty adapterPosition(int adapterPosition) {
            mAdapterPosition = adapterPosition;
            return this;
        };

        /**
         * Sets view Id of a descendant of recyclerview child to track.
         *
         * @param viewId Id of a descendant of recyclerview child.
         * @return This ChildPositionProperty object.
         */
        public ChildPositionProperty viewId(int viewId) {
            mViewId = viewId;
            return this;
        }

        /**
         * Sets offset in pixels added to the view's start position.
         *
         * @param offset Offset in pixels added to the view's start position.
         * @return This ChildPositionProperty object.
         */
        public ChildPositionProperty offset(int offset) {
            mOffset = offset;
            return this;
        }

        /**
         * Sets fraction of size to be added to view's start position.  e.g. to track the
         * center position of the view, use fraction 0.5; to track the end position of the view
         * use fraction 1.
         *
         * @param fraction Fraction of size of the view.
         * @return This ChildPositionProperty object.
         */
        public ChildPositionProperty fraction(float fraction) {
            mFraction = fraction;
            return this;
        }

        /**
         * Returns adapter position of the recyclerview child to track.
         */
        public int getAdapterPosition() {
            return mAdapterPosition;
        }

        /**
         * Returns view Id of a descendant of recyclerview child to track.
         */
        public int getViewId() {
            return mViewId;
        }

        /**
         * Returns offset in pixels added to the view's start position.
         */
        public int getOffset() {
            return mOffset;
        }

        /**
         * Returns fraction of size to be added to view's start position.  e.g. to track the
         * center position of the view, use fraction 0.5; to track the end position of the view
         * use fraction 1.
         */
        public float getFraction() {
            return mFraction;
        }

        void updateValue(RecyclerViewParallax source) {
            RecyclerView recyclerView = source.mRecylerView;
            ViewHolder viewHolder = recyclerView == null ? null
                    : recyclerView.findViewHolderForAdapterPosition(mAdapterPosition);
            if (viewHolder == null) {
                if (recyclerView == null || recyclerView.getLayoutManager().getChildCount() == 0) {
                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
                    return;
                }
                View firstChild = recyclerView.getLayoutManager().getChildAt(0);
                ViewHolder vh = recyclerView.findContainingViewHolder(firstChild);
                int firstPosition = vh.getAbsoluteAdapterPosition();
                if (firstPosition < mAdapterPosition) {
                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
                } else {
                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_BEFORE);
                }
            } else {
                View trackingView = viewHolder.itemView.findViewById(mViewId);
                if (trackingView == null) {
                    return;
                }

                Rect rect = new Rect(
                        0, 0, trackingView.getWidth(), trackingView.getHeight());
                recyclerView.offsetDescendantRectToMyCoords(trackingView, rect);
                // Slide transition may change the trackingView's translationX/translationY,
                // add up translation values in parent.
                float tx = 0, ty = 0;
                while (trackingView != recyclerView && trackingView != null) {
                    // In RecyclerView dispatchLayout() it may call onScrolled(0) with a move
                    // ItemAnimation just created. We don't have any way to track the ItemAnimation
                    // update listener, and in ideal use case, the tracking view should not be
                    // animated in RecyclerView. Do not apply translation value for this case.
                    if (!(trackingView.getParent() == recyclerView && recyclerView.isAnimating())) {
                        tx += trackingView.getTranslationX();
                        ty += trackingView.getTranslationY();
                    }
                    trackingView = (View) trackingView.getParent();
                }
                rect.offset((int) tx, (int) ty);
                if (source.mIsVertical) {
                    source.setIntPropertyValue(getIndex(), rect.top + mOffset
                            + (int) (mFraction * rect.height()));
                } else {
                    source.setIntPropertyValue(getIndex(), rect.left + mOffset
                            + (int) (mFraction * rect.width()));
                }
            }
        }
    }


    @Override
    public ChildPositionProperty createProperty(String name, int index) {
        return new ChildPositionProperty(name, index);
    }

    @Override
    public float getMaxValue() {
        if (mRecylerView == null) {
            return 0;
        }
        return mIsVertical ? mRecylerView.getHeight() : mRecylerView.getWidth();
    }

    /**
     * Set RecyclerView that this Parallax will register onScrollListener.
     * @param recyclerView RecyclerView to register onScrollListener.
     */
    public void setRecyclerView(RecyclerView recyclerView) {
        if (mRecylerView == recyclerView) {
            return;
        }
        if (mRecylerView != null) {
            mRecylerView.removeOnScrollListener(mOnScrollListener);
            mRecylerView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
        }
        mRecylerView = recyclerView;
        if (mRecylerView != null) {
            LayoutManager.Properties properties = mRecylerView.getLayoutManager()
                    .getProperties(mRecylerView.getContext(), null, 0, 0);
            mIsVertical = properties.orientation == RecyclerView.VERTICAL;
            mRecylerView.addOnScrollListener(mOnScrollListener);
            mRecylerView.addOnLayoutChangeListener(mOnLayoutChangeListener);
        }
    }

    /**
     * Manually update values. This is used for changes not controlled by RecyclerView. E.g.
     * called by a Slide transition that changes translation of the view.
     */
    @Override
    public void updateValues() {
        for (Property prop: getProperties()) {
            ((ChildPositionProperty) prop).updateValue(RecyclerViewParallax.this);
        }
        super.updateValues();
    }

    /**
     * @return Currently RecylerView that the source has registered onScrollListener.
     */
    public RecyclerView getRecyclerView() {
        return mRecylerView;
    }
}