/* * Copyright (C) 2015 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 com.android.tv.menu; import android.content.Context; import androidx.leanback.widget.HorizontalGridView; import androidx.leanback.widget.OnChildSelectedListener; import androidx.recyclerview.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.android.tv.MainActivity; import com.android.tv.R; import com.android.tv.util.ViewCache; import java.util.Collections; import java.util.List; /** A view that shows a title and list view. */ public class ItemListRowView extends MenuRowView implements OnChildSelectedListener { private static final String TAG = MenuView.TAG; private static final boolean DEBUG = MenuView.DEBUG; public interface CardView { void onBind(T row, boolean selected); void onRecycled(); void onSelected(); void onDeselected(); boolean requestFocusWithAccessibility(); } private HorizontalGridView mListView; private CardView mSelectedCard; public ItemListRowView(Context context) { this(context, null); } public ItemListRowView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ItemListRowView(Context context, AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, 0); } public ItemListRowView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onFinishInflate() { super.onFinishInflate(); mListView = (HorizontalGridView) getContentsView(); // Disable the position change animation of the cards. mListView.setItemAnimator(null); } @Override protected int getContentsViewId() { return R.id.list_view; } @Override public void onBind(MenuRow row) { super.onBind(row); ItemListAdapter adapter = ((ItemListRow) row).getAdapter(); adapter.mItemListView = this; mListView.setOnChildSelectedListener(this); mListView.setAdapter(adapter); } @Override public void initialize(int reason) { super.initialize(reason); setInitialFocusView(mListView); mListView.setSelectedPosition(getAdapter().getInitialPosition()); } private ItemListAdapter getAdapter() { return (ItemListAdapter) mListView.getAdapter(); } @Override public void onChildSelected(ViewGroup parent, View child, int position, long id) { if (DEBUG) Log.d(TAG, "onChildSelected: child=" + child); if (mSelectedCard == child) { return; } if (mSelectedCard != null) { mSelectedCard.onDeselected(); } mSelectedCard = (CardView) child; if (mSelectedCard != null) { mSelectedCard.onSelected(); } } @Override protected void requestChildFocus() { if (mSelectedCard != null) { mSelectedCard.requestFocusWithAccessibility(); } } public abstract static class ItemListAdapter extends RecyclerView.Adapter { private final MainActivity mMainActivity; private final LayoutInflater mLayoutInflater; private List mItemList = Collections.emptyList(); private ItemListRowView mItemListView; public ItemListAdapter(Context context) { // Only MainActivity can use the main menu. mMainActivity = (MainActivity) context; mLayoutInflater = LayoutInflater.from(context); } /** * In most cases, implementation should call {@link #setItemList(java.util.List)} with newly * update item list. */ public abstract void update(); /** Gets layout resource ID. It'll be used in {@link #onCreateViewHolder}. */ protected abstract int getLayoutResId(int viewType); /** Releases all the resources which need to be released. */ public void release() {} /** * The initial position of list that will be selected when the main menu appears. By * default, the first item is initially selected. */ public int getInitialPosition() { return 0; } /** The MainActivity that the corresponding ItemListView belongs to. */ protected MainActivity getMainActivity() { return mMainActivity; } /** The item list. */ protected List getItemList() { return mItemList; } /** * Sets the item list. * *

This sends an item change event, not a structural change event. The items of the same * positions retain the same identity. * *

If there's any structural change and relayout and rebind is needed, call {@link * #notifyDataSetChanged} explicitly. */ protected void setItemList(List itemList) { int oldSize = mItemList.size(); int newSize = itemList.size(); mItemList = itemList; if (oldSize > newSize) { notifyItemRangeChanged(0, newSize); notifyItemRangeRemoved(newSize, oldSize - newSize); } else if (oldSize < newSize) { notifyItemRangeChanged(0, oldSize); notifyItemRangeInserted(oldSize, newSize - oldSize); } else { notifyItemRangeChanged(0, oldSize); } } @Override public int getItemViewType(int position) { return 0; } @Override public int getItemCount() { return mItemList.size(); } /** Returns the position of the item. */ protected int getItemPosition(T item) { return mItemList.indexOf(item); } /** Returns {@code true} if the item list contains the item, otherwise {@code false}. */ protected boolean containsItem(T item) { return mItemList.contains(item); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = ViewCache.getInstance() .getOrCreateView(mLayoutInflater, getLayoutResId(viewType), parent); return new MyViewHolder(view); } @Override public void onBindViewHolder(MyViewHolder viewHolder, int position) { @SuppressWarnings("unchecked") CardView cardView = (CardView) viewHolder.itemView; cardView.onBind(mItemList.get(position), cardView.equals(mItemListView.mSelectedCard)); } @Override public void onViewRecycled(MyViewHolder viewHolder) { super.onViewRecycled(viewHolder); CardView cardView = (CardView) viewHolder.itemView; cardView.onRecycled(); } public static class MyViewHolder extends RecyclerView.ViewHolder { public MyViewHolder(View view) { super(view); } } } }