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 */ 16 package androidx.wear.widget; 17 18 import android.content.Context; 19 import android.view.View; 20 21 import androidx.recyclerview.widget.LinearLayoutManager; 22 import androidx.recyclerview.widget.RecyclerView; 23 24 import org.jspecify.annotations.Nullable; 25 26 /** 27 * This wear-specific implementation of {@link LinearLayoutManager} provides basic 28 * offsetting logic for updating child layout. For round devices it offsets the children 29 * horizontally to make them appear to travel around a circle. For square devices it aligns them in 30 * a straight list. This functionality is provided by the {@link CurvingLayoutCallback} which is 31 * set when constructing the this class with its default constructor 32 * {@link #WearableLinearLayoutManager(Context)}. 33 */ 34 public class WearableLinearLayoutManager extends LinearLayoutManager { 35 36 private @Nullable LayoutCallback mLayoutCallback; 37 38 /** 39 * Callback for interacting with layout passes. 40 */ 41 public abstract static class LayoutCallback { 42 /** 43 * Override this method to implement custom child layout behavior on scroll. It is called 44 * at the end of each layout pass of the view (including scrolling) and enables you to 45 * modify any property of the child view. Examples include scaling the children based on 46 * their distance from the center of the parent, or changing the translation of the children 47 * to create an illusion of the path they are moving along. 48 * 49 * @param child the current child to be affected. 50 * @param parent the {@link RecyclerView} parent that this class is attached to. 51 */ onLayoutFinished(View child, RecyclerView parent)52 public abstract void onLayoutFinished(View child, RecyclerView parent); 53 } 54 55 /** 56 * Creates a {@link WearableLinearLayoutManager} for a vertical list. 57 * 58 * @param context Current context, will be used to access resources. 59 * @param layoutCallback Callback to be associated with this {@link WearableLinearLayoutManager} 60 */ WearableLinearLayoutManager(Context context, LayoutCallback layoutCallback)61 public WearableLinearLayoutManager(Context context, LayoutCallback layoutCallback) { 62 super(context, VERTICAL, false); 63 mLayoutCallback = layoutCallback; 64 } 65 66 /** 67 * Creates a {@link WearableLinearLayoutManager} for a vertical list. 68 * 69 * @param context Current context, will be used to access resources. 70 */ WearableLinearLayoutManager(Context context)71 public WearableLinearLayoutManager(Context context) { 72 this(context, new CurvingLayoutCallback(context)); 73 } 74 75 /** 76 * Set a particular instance of the layout callback for this 77 * {@link WearableLinearLayoutManager}. The callback will be called on the Ui thread. 78 * 79 * @param layoutCallback 80 */ setLayoutCallback(@ullable LayoutCallback layoutCallback)81 public void setLayoutCallback(@Nullable LayoutCallback layoutCallback) { 82 mLayoutCallback = layoutCallback; 83 } 84 85 /** 86 * @return the current {@link LayoutCallback} associated with this 87 * {@link WearableLinearLayoutManager}. 88 */ getLayoutCallback()89 public @Nullable LayoutCallback getLayoutCallback() { 90 return mLayoutCallback; 91 } 92 93 @Override scrollVerticallyBy( int dy, RecyclerView.Recycler recycler, RecyclerView.State state)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 onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)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 updateLayout()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