• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
17 package com.android.tv.menu;
18 
19 import android.content.Context;
20 import android.support.v17.leanback.widget.HorizontalGridView;
21 import android.support.v17.leanback.widget.OnChildSelectedListener;
22 import android.support.v7.widget.RecyclerView;
23 import android.util.AttributeSet;
24 import android.util.Log;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import com.android.tv.MainActivity;
29 import com.android.tv.R;
30 import com.android.tv.util.ViewCache;
31 import java.util.Collections;
32 import java.util.List;
33 
34 /** A view that shows a title and list view. */
35 public class ItemListRowView extends MenuRowView implements OnChildSelectedListener {
36     private static final String TAG = MenuView.TAG;
37     private static final boolean DEBUG = MenuView.DEBUG;
38 
39     public interface CardView<T> {
onBind(T row, boolean selected)40         void onBind(T row, boolean selected);
41 
onRecycled()42         void onRecycled();
43 
onSelected()44         void onSelected();
45 
onDeselected()46         void onDeselected();
47     }
48 
49     private HorizontalGridView mListView;
50     private CardView<?> mSelectedCard;
51 
ItemListRowView(Context context)52     public ItemListRowView(Context context) {
53         this(context, null);
54     }
55 
ItemListRowView(Context context, AttributeSet attrs)56     public ItemListRowView(Context context, AttributeSet attrs) {
57         this(context, attrs, 0);
58     }
59 
ItemListRowView(Context context, AttributeSet attrs, int defStyle)60     public ItemListRowView(Context context, AttributeSet attrs, int defStyle) {
61         this(context, attrs, defStyle, 0);
62     }
63 
ItemListRowView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)64     public ItemListRowView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
65         super(context, attrs, defStyleAttr, defStyleRes);
66     }
67 
68     @Override
onFinishInflate()69     protected void onFinishInflate() {
70         super.onFinishInflate();
71         mListView = (HorizontalGridView) getContentsView();
72         // Disable the position change animation of the cards.
73         mListView.setItemAnimator(null);
74     }
75 
76     @Override
getContentsViewId()77     protected int getContentsViewId() {
78         return R.id.list_view;
79     }
80 
81     @Override
onBind(MenuRow row)82     public void onBind(MenuRow row) {
83         super.onBind(row);
84         ItemListAdapter<?> adapter = ((ItemListRow) row).getAdapter();
85         adapter.mItemListView = this;
86 
87         mListView.setOnChildSelectedListener(this);
88         mListView.setAdapter(adapter);
89     }
90 
91     @Override
initialize(int reason)92     public void initialize(int reason) {
93         super.initialize(reason);
94         setInitialFocusView(mListView);
95         mListView.setSelectedPosition(getAdapter().getInitialPosition());
96     }
97 
getAdapter()98     private ItemListAdapter<?> getAdapter() {
99         return (ItemListAdapter<?>) mListView.getAdapter();
100     }
101 
102     @Override
onChildSelected(ViewGroup parent, View child, int position, long id)103     public void onChildSelected(ViewGroup parent, View child, int position, long id) {
104         if (DEBUG) Log.d(TAG, "onChildSelected: child=" + child);
105         if (mSelectedCard == child) {
106             return;
107         }
108         if (mSelectedCard != null) {
109             mSelectedCard.onDeselected();
110         }
111         mSelectedCard = (CardView<?>) child;
112         if (mSelectedCard != null) {
113             mSelectedCard.onSelected();
114         }
115     }
116 
117     public abstract static class ItemListAdapter<T>
118             extends RecyclerView.Adapter<ItemListAdapter.MyViewHolder> {
119         private final MainActivity mMainActivity;
120         private final LayoutInflater mLayoutInflater;
121         private List<T> mItemList = Collections.emptyList();
122         private ItemListRowView mItemListView;
123 
ItemListAdapter(Context context)124         public ItemListAdapter(Context context) {
125             // Only MainActivity can use the main menu.
126             mMainActivity = (MainActivity) context;
127             mLayoutInflater = LayoutInflater.from(context);
128         }
129 
130         /**
131          * In most cases, implementation should call {@link #setItemList(java.util.List)} with newly
132          * update item list.
133          */
update()134         public abstract void update();
135 
136         /** Gets layout resource ID. It'll be used in {@link #onCreateViewHolder}. */
getLayoutResId(int viewType)137         protected abstract int getLayoutResId(int viewType);
138 
139         /** Releases all the resources which need to be released. */
release()140         public void release() {}
141 
142         /**
143          * The initial position of list that will be selected when the main menu appears. By
144          * default, the first item is initially selected.
145          */
getInitialPosition()146         public int getInitialPosition() {
147             return 0;
148         }
149 
150         /** The MainActivity that the corresponding ItemListView belongs to. */
getMainActivity()151         protected MainActivity getMainActivity() {
152             return mMainActivity;
153         }
154 
155         /** The item list. */
getItemList()156         protected List<T> getItemList() {
157             return mItemList;
158         }
159 
160         /**
161          * Sets the item list.
162          *
163          * <p>This sends an item change event, not a structural change event. The items of the same
164          * positions retain the same identity.
165          *
166          * <p>If there's any structural change and relayout and rebind is needed, call {@link
167          * #notifyDataSetChanged} explicitly.
168          */
setItemList(List<T> itemList)169         protected void setItemList(List<T> itemList) {
170             int oldSize = mItemList.size();
171             int newSize = itemList.size();
172             mItemList = itemList;
173             if (oldSize > newSize) {
174                 notifyItemRangeChanged(0, newSize);
175                 notifyItemRangeRemoved(newSize, oldSize - newSize);
176             } else if (oldSize < newSize) {
177                 notifyItemRangeChanged(0, oldSize);
178                 notifyItemRangeInserted(oldSize, newSize - oldSize);
179             } else {
180                 notifyItemRangeChanged(0, oldSize);
181             }
182         }
183 
184         @Override
getItemViewType(int position)185         public int getItemViewType(int position) {
186             return 0;
187         }
188 
189         @Override
getItemCount()190         public int getItemCount() {
191             return mItemList.size();
192         }
193 
194         /** Returns the position of the item. */
getItemPosition(T item)195         protected int getItemPosition(T item) {
196             return mItemList.indexOf(item);
197         }
198 
199         /** Returns {@code true} if the item list contains the item, otherwise {@code false}. */
containsItem(T item)200         protected boolean containsItem(T item) {
201             return mItemList.contains(item);
202         }
203 
204         @Override
onCreateViewHolder(ViewGroup parent, int viewType)205         public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
206             View view =
207                     ViewCache.getInstance()
208                             .getOrCreateView(mLayoutInflater, getLayoutResId(viewType), parent);
209             return new MyViewHolder(view);
210         }
211 
212         @Override
onBindViewHolder(MyViewHolder viewHolder, int position)213         public void onBindViewHolder(MyViewHolder viewHolder, int position) {
214             @SuppressWarnings("unchecked")
215             CardView<T> cardView = (CardView<T>) viewHolder.itemView;
216             cardView.onBind(mItemList.get(position), cardView.equals(mItemListView.mSelectedCard));
217         }
218 
219         @Override
onViewRecycled(MyViewHolder viewHolder)220         public void onViewRecycled(MyViewHolder viewHolder) {
221             super.onViewRecycled(viewHolder);
222             CardView<T> cardView = (CardView<T>) viewHolder.itemView;
223             cardView.onRecycled();
224         }
225 
226         public static class MyViewHolder extends RecyclerView.ViewHolder {
MyViewHolder(View view)227             public MyViewHolder(View view) {
228                 super(view);
229             }
230         }
231     }
232 }
233