blob: 9c882ab56de8ee9dc8bf6345cddc785884f348ce [file] [log] [blame]
/*
* Copyright (C) 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 android.support.wear.widget;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* This wear-specific implementation of {@link LinearLayoutManager} provides basic
* offsetting logic for updating child layout. For round devices it offsets the children
* horizontally to make them appear to travel around a circle. For square devices it aligns them in
* a straight list. This functionality is provided by the {@link CurvingLayoutCallback} which is
* set when constructing the this class with its default constructor
* {@link #WearableLinearLayoutManager(Context)}.
*/
public class WearableLinearLayoutManager extends LinearLayoutManager {
@Nullable
private LayoutCallback mLayoutCallback;
/**
* Callback for interacting with layout passes.
*/
public abstract static class LayoutCallback {
/**
* Override this method to implement custom child layout behavior on scroll. It is called
* at the end of each layout pass of the view (including scrolling) and enables you to
* modify any property of the child view. Examples include scaling the children based on
* their distance from the center of the parent, or changing the translation of the children
* to create an illusion of the path they are moving along.
*
* @param child the current child to be affected.
* @param parent the {@link RecyclerView} parent that this class is attached to.
*/
public abstract void onLayoutFinished(View child, RecyclerView parent);
}
/**
* Creates a {@link WearableLinearLayoutManager} for a vertical list.
*
* @param context Current context, will be used to access resources.
* @param layoutCallback Callback to be associated with this {@link WearableLinearLayoutManager}
*/
public WearableLinearLayoutManager(Context context, LayoutCallback layoutCallback) {
super(context, VERTICAL, false);
mLayoutCallback = layoutCallback;
}
/**
* Creates a {@link WearableLinearLayoutManager} for a vertical list.
*
* @param context Current context, will be used to access resources.
*/
public WearableLinearLayoutManager(Context context) {
this(context, new CurvingLayoutCallback(context));
}
/**
* Set a particular instance of the layout callback for this
* {@link WearableLinearLayoutManager}. The callback will be called on the Ui thread.
*
* @param layoutCallback
*/
public void setLayoutCallback(@Nullable LayoutCallback layoutCallback) {
mLayoutCallback = layoutCallback;
}
/**
* @return the current {@link LayoutCallback} associated with this
* {@link WearableLinearLayoutManager}.
*/
@Nullable
public LayoutCallback getLayoutCallback() {
return mLayoutCallback;
}
@Override
public int scrollVerticallyBy(
int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
int scrolled = super.scrollVerticallyBy(dy, recycler, state);
updateLayout();
return scrolled;
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
if (getChildCount() == 0) {
return;
}
updateLayout();
}
private void updateLayout() {
if (mLayoutCallback == null) {
return;
}
final int childCount = getChildCount();
for (int count = 0; count < childCount; count++) {
View child = getChildAt(count);
mLayoutCallback.onLayoutFinished(child, (WearableRecyclerView) child.getParent());
}
}
}