/* * 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 android.support.v17.leanback.widget; import android.support.v17.leanback.tests.R; import android.support.v7.widget.RecyclerView; import android.support.v17.leanback.widget.BaseGridView; import android.support.v17.leanback.widget.OnChildSelectedListener; import android.app.Activity; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; /** * @hide from javadoc */ public class GridActivity extends Activity { private static final String TAG = "GridActivity"; public static final String EXTRA_LAYOUT_RESOURCE_ID = "layoutResourceId"; public static final String EXTRA_NUM_ITEMS = "numItems"; public static final String EXTRA_ITEMS = "items"; public static final String EXTRA_ITEMS_FOCUSABLE = "itemsFocusable"; public static final String EXTRA_STAGGERED = "staggered"; public static final String EXTRA_REQUEST_LAYOUT_ONFOCUS = "requestLayoutOnFocus"; public static final String EXTRA_REQUEST_FOCUS_ONLAYOUT = "requstFocusOnLayout"; public static final String EXTRA_CHILD_LAYOUT_ID = "childLayoutId"; public static final String EXTRA_SECONDARY_SIZE_ZERO = "secondarySizeZero"; /** * Class that implements GridWidgetTest.ViewTypeProvider for creating different * view types for each position. */ public static final String EXTRA_VIEWTYPEPROVIDER_CLASS = "viewtype_class"; /** * Class that implements GridWidgetTest.ItemAlignmentFacetProvider for creating different * ItemAlignmentFacet for each ViewHolder. */ public static final String EXTRA_ITEMALIGNMENTPROVIDER_CLASS = "itemalignment_class"; /** * Class that implements GridWidgetTest.ItemAlignmentFacetProvider for creating different * ItemAlignmentFacet for a given viewType. */ public static final String EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS = "itemalignment_viewtype_class"; public static final String SELECT_ACTION = "android.test.leanback.widget.SELECT"; static final int DEFAULT_NUM_ITEMS = 100; static final boolean DEFAULT_STAGGERED = true; static final boolean DEFAULT_REQUEST_LAYOUT_ONFOCUS = false; static final boolean DEFAULT_REQUEST_FOCUS_ONLAYOUT = false; private static final boolean DEBUG = false; int mLayoutId; int mOrientation; int mNumItems; int mChildLayout; boolean mStaggered; boolean mRequestLayoutOnFocus; boolean mRequestFocusOnLayout; boolean mSecondarySizeZero; GridWidgetTest.ViewTypeProvider mViewTypeProvider; GridWidgetTest.ItemAlignmentFacetProvider mAlignmentProvider; GridWidgetTest.ItemAlignmentFacetProvider mAlignmentViewTypeProvider; int[] mGridViewLayoutSize; BaseGridView mGridView; int[] mItemLengths; boolean[] mItemFocusables; private int mBoundCount; private View createView() { View view = getLayoutInflater().inflate(mLayoutId, null, false); mGridView = (BaseGridView) view.findViewById(R.id.gridview); mOrientation = mGridView instanceof HorizontalGridView ? BaseGridView.HORIZONTAL : BaseGridView.VERTICAL; mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_BOTH_EDGE); mGridView.setWindowAlignmentOffsetPercent(35); mGridView.setOnChildSelectedListener(new OnChildSelectedListener() { @Override public void onChildSelected(ViewGroup parent, View view, int position, long id) { if (DEBUG) Log.d(TAG, "onChildSelected position=" + position + " id="+id); } }); return view; } @Override protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); mLayoutId = intent.getIntExtra(EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_grid); mChildLayout = intent.getIntExtra(EXTRA_CHILD_LAYOUT_ID, -1); mStaggered = intent.getBooleanExtra(EXTRA_STAGGERED, DEFAULT_STAGGERED); mRequestLayoutOnFocus = intent.getBooleanExtra(EXTRA_REQUEST_LAYOUT_ONFOCUS, DEFAULT_REQUEST_LAYOUT_ONFOCUS); mRequestFocusOnLayout = intent.getBooleanExtra(EXTRA_REQUEST_FOCUS_ONLAYOUT, DEFAULT_REQUEST_FOCUS_ONLAYOUT); mSecondarySizeZero = intent.getBooleanExtra(EXTRA_SECONDARY_SIZE_ZERO, false); mItemLengths = intent.getIntArrayExtra(EXTRA_ITEMS); mItemFocusables = intent.getBooleanArrayExtra(EXTRA_ITEMS_FOCUSABLE); String alignmentClass = intent.getStringExtra(EXTRA_ITEMALIGNMENTPROVIDER_CLASS); String alignmentViewTypeClass = intent.getStringExtra(EXTRA_ITEMALIGNMENTPROVIDER_VIEWTYPE_CLASS); String viewTypeClass = intent.getStringExtra(EXTRA_VIEWTYPEPROVIDER_CLASS); try { if (alignmentClass != null) { mAlignmentProvider = (GridWidgetTest.ItemAlignmentFacetProvider) Class.forName(alignmentClass).newInstance(); } if (alignmentViewTypeClass != null) { mAlignmentViewTypeProvider = (GridWidgetTest.ItemAlignmentFacetProvider) Class.forName(alignmentViewTypeClass).newInstance(); } if (viewTypeClass != null) { mViewTypeProvider = (GridWidgetTest.ViewTypeProvider) Class.forName(viewTypeClass).newInstance(); } } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } super.onCreate(savedInstanceState); if (DEBUG) Log.v(TAG, "onCreate " + this); RecyclerView.Adapter adapter = new MyAdapter(); View view = createView(); if (mItemLengths == null) { mNumItems = intent.getIntExtra(EXTRA_NUM_ITEMS, DEFAULT_NUM_ITEMS); mItemLengths = new int[mNumItems]; for (int i = 0; i < mItemLengths.length; i++) { if (mOrientation == BaseGridView.HORIZONTAL) { mItemLengths[i] = mStaggered ? (int)(Math.random() * 180) + 180 : 240; } else { mItemLengths[i] = mStaggered ? (int)(Math.random() * 120) + 120 : 160; } } } else { mNumItems = mItemLengths.length; } mGridView.setAdapter(new MyAdapter()); setContentView(view); } void rebindToNewAdapter() { mGridView.setAdapter(new MyAdapter()); } @Override protected void onNewIntent(Intent intent) { if (DEBUG) Log.v(TAG, "onNewIntent " + intent+ " "+this); if (intent.getAction().equals(SELECT_ACTION)) { int position = intent.getIntExtra("SELECT_POSITION", -1); if (position >= 0) { mGridView.setSelectedPosition(position); } } super.onNewIntent(intent); } private OnFocusChangeListener mItemFocusChangeListener = new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { v.setBackgroundColor(Color.YELLOW); } else { v.setBackgroundColor(Color.LTGRAY); } if (mRequestLayoutOnFocus) { RecyclerView.ViewHolder vh = mGridView.getChildViewHolder(v); int position = vh.getAdapterPosition(); updateSize(v, position); } } }; private OnFocusChangeListener mSubItemFocusChangeListener = new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { v.setBackgroundColor(Color.YELLOW); } else { v.setBackgroundColor(Color.LTGRAY); } } }; void resetBoundCount() { mBoundCount = 0; } int getBoundCount() { return mBoundCount; } void swap(int index1, int index2) { if (index1 == index2) { return; } else if (index1 > index2) { int index = index1; index1 = index2; index2 = index; } int value = mItemLengths[index1]; mItemLengths[index1] = mItemLengths[index2]; mItemLengths[index2] = value; mGridView.getAdapter().notifyItemMoved(index1, index2); mGridView.getAdapter().notifyItemMoved(index2 - 1, index1); } void changeArraySize(int length) { mNumItems = length; mGridView.getAdapter().notifyDataSetChanged(); } int[] removeItems(int index, int length) { int[] removed = new int[length]; System.arraycopy(mItemLengths, index, removed, 0, length); System.arraycopy(mItemLengths, index + length, mItemLengths, index, mNumItems - index - length); mNumItems -= length; mGridView.getAdapter().notifyItemRangeRemoved(index, length); return removed; } void addItems(int index, int[] items) { int length = items.length; if (mItemLengths.length < mNumItems + length) { int[] array = new int[mNumItems + length]; System.arraycopy(mItemLengths, 0, array, 0, mNumItems); mItemLengths = array; } System.arraycopy(mItemLengths, index, mItemLengths, index + length, mNumItems - index); System.arraycopy(items, 0, mItemLengths, index, length); mNumItems += length; mGridView.getAdapter().notifyItemRangeInserted(index, length); } class MyAdapter extends RecyclerView.Adapter implements FacetProviderAdapter { @Override public int getItemViewType(int position) { if (mViewTypeProvider != null) { return mViewTypeProvider.getViewType(position); } return 0; } @Override public FacetProvider getFacetProvider(int viewType) { final Object alignmentFacet = mAlignmentViewTypeProvider != null? mAlignmentViewTypeProvider.getItemAlignmentFacet(viewType) : null; if (alignmentFacet != null) { return new FacetProvider() { @Override public Object getFacet(Class facetClass) { if (facetClass.equals(ItemAlignmentFacet.class)) { return alignmentFacet; } return null; } }; } return null; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (DEBUG) Log.v(TAG, "createViewHolder " + viewType); if (mChildLayout != -1) { final View view = getLayoutInflater().inflate(mChildLayout, null, false); ArrayList<View> focusables = new ArrayList<View>(); view.addFocusables(focusables, View.FOCUS_UP); for (int i = 0; i < focusables.size(); i++) { View f = focusables.get(i); f.setBackgroundColor(Color.LTGRAY); f.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { v.setBackgroundColor(Color.YELLOW); } else { v.setBackgroundColor(Color.LTGRAY); } if (mRequestLayoutOnFocus) { if (v == view) { RecyclerView.ViewHolder vh = mGridView.getChildViewHolder(v); int position = vh.getAdapterPosition(); updateSize(v, position); } view.requestLayout(); } } }); } ViewHolder holder = new ViewHolder(view); return holder; } TextView textView = new TextView(parent.getContext()) { @Override protected void onLayout(boolean change, int left, int top, int right, int bottom) { super.onLayout(change, left, top, right, bottom); if (mRequestFocusOnLayout) { if (hasFocus()) { clearFocus(); requestFocus(); } } } }; textView.setTextColor(Color.BLACK); textView.setOnFocusChangeListener(mItemFocusChangeListener); return new ViewHolder(textView); } @Override public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) { if (DEBUG) Log.v(TAG, "bindViewHolder " + position + " " + baseHolder); mBoundCount++; ViewHolder holder = (ViewHolder) baseHolder; if (mAlignmentProvider != null) { holder.mItemAlignment = mAlignmentProvider.getItemAlignmentFacet(position); } else { holder.mItemAlignment = null; } if (mChildLayout == -1) { ((TextView) holder.itemView).setText("Item "+mItemLengths[position]); boolean focusable = true; if (mItemFocusables != null) { focusable = mItemFocusables[position]; } ((TextView) holder.itemView).setFocusable(focusable); ((TextView) holder.itemView).setFocusableInTouchMode(focusable); holder.itemView.setBackgroundColor(Color.LTGRAY); } else { if (holder.itemView instanceof TextView) { ((TextView) holder.itemView).setText("Item "+mItemLengths[position]); } } updateSize(holder.itemView, position); } @Override public int getItemCount() { return mNumItems; } } void updateSize(View view, int position) { ViewGroup.LayoutParams p = view.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(0, 0); } if (mOrientation == BaseGridView.HORIZONTAL) { p.width = mItemLengths[position] + (mRequestLayoutOnFocus && view.hasFocus() ? 1 : 0); p.height = mSecondarySizeZero ? 0 : 80; } else { p.width = mSecondarySizeZero ? 0 : 240; p.height = mItemLengths[position] + (mRequestLayoutOnFocus && view.hasFocus() ? 1 : 0); } view.setLayoutParams(p); } static class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider { ItemAlignmentFacet mItemAlignment; public ViewHolder(View v) { super(v); } @Override public Object getFacet(Class facetClass) { if (facetClass.equals(ItemAlignmentFacet.class)) { return mItemAlignment; } return null; } } }