1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.support.wear.widget; 17 18import android.content.Context; 19import android.support.annotation.Nullable; 20import android.support.v7.widget.LinearLayoutManager; 21import android.support.v7.widget.RecyclerView; 22import android.view.View; 23 24/** 25 * This wear-specific implementation of {@link LinearLayoutManager} provides basic 26 * offsetting logic for updating child layout. For round devices it offsets the children 27 * horizontally to make them appear to travel around a circle. For square devices it aligns them in 28 * a straight list. This functionality is provided by the {@link CurvingLayoutCallback} which is 29 * set when constructing the this class with its default constructor 30 * {@link #WearableLinearLayoutManager(Context)}. 31 */ 32public class WearableLinearLayoutManager extends LinearLayoutManager { 33 34 @Nullable 35 private LayoutCallback mLayoutCallback; 36 37 /** 38 * Callback for interacting with layout passes. 39 */ 40 public abstract static class LayoutCallback { 41 /** 42 * Override this method to implement custom child layout behavior on scroll. It is called 43 * at the end of each layout pass of the view (including scrolling) and enables you to 44 * modify any property of the child view. Examples include scaling the children based on 45 * their distance from the center of the parent, or changing the translation of the children 46 * to create an illusion of the path they are moving along. 47 * 48 * @param child the current child to be affected. 49 * @param parent the {@link RecyclerView} parent that this class is attached to. 50 */ 51 public abstract void onLayoutFinished(View child, RecyclerView parent); 52 } 53 54 /** 55 * Creates a {@link WearableLinearLayoutManager} for a vertical list. 56 * 57 * @param context Current context, will be used to access resources. 58 * @param layoutCallback Callback to be associated with this {@link WearableLinearLayoutManager} 59 */ 60 public WearableLinearLayoutManager(Context context, LayoutCallback layoutCallback) { 61 super(context, VERTICAL, false); 62 mLayoutCallback = layoutCallback; 63 } 64 65 /** 66 * Creates a {@link WearableLinearLayoutManager} for a vertical list. 67 * 68 * @param context Current context, will be used to access resources. 69 */ 70 public WearableLinearLayoutManager(Context context) { 71 this(context, new CurvingLayoutCallback(context)); 72 } 73 74 /** 75 * Set a particular instance of the layout callback for this 76 * {@link WearableLinearLayoutManager}. The callback will be called on the Ui thread. 77 * 78 * @param layoutCallback 79 */ 80 public void setLayoutCallback(@Nullable LayoutCallback layoutCallback) { 81 mLayoutCallback = layoutCallback; 82 } 83 84 /** 85 * @return the current {@link LayoutCallback} associated with this 86 * {@link WearableLinearLayoutManager}. 87 */ 88 @Nullable 89 public LayoutCallback getLayoutCallback() { 90 return mLayoutCallback; 91 } 92 93 @Override 94 public int scrollVerticallyBy( 95 int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 96 int scrolled = super.scrollVerticallyBy(dy, recycler, state); 97 98 updateLayout(); 99 return scrolled; 100 } 101 102 @Override 103 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 104 super.onLayoutChildren(recycler, state); 105 if (getChildCount() == 0) { 106 return; 107 } 108 109 updateLayout(); 110 } 111 112 private void updateLayout() { 113 if (mLayoutCallback == null) { 114 return; 115 } 116 final int childCount = getChildCount(); 117 for (int count = 0; count < childCount; count++) { 118 View child = getChildAt(count); 119 mLayoutCallback.onLayoutFinished(child, (WearableRecyclerView) child.getParent()); 120 } 121 } 122} 123