HeadersFragment.java revision 9a49e322d10c1a67860d812352a5ea710843b75e
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package android.support.v17.leanback.app; 16 17import android.content.Context; 18import android.graphics.Color; 19import android.graphics.drawable.ColorDrawable; 20import android.graphics.drawable.Drawable; 21import android.graphics.drawable.GradientDrawable; 22import android.os.Bundle; 23import android.support.v17.leanback.R; 24import android.support.v17.leanback.widget.FocusHighlightHelper; 25import android.support.v17.leanback.widget.ItemBridgeAdapter; 26import android.support.v17.leanback.widget.PresenterSelector; 27import android.support.v17.leanback.widget.OnItemViewSelectedListener; 28import android.support.v17.leanback.widget.Row; 29import android.support.v17.leanback.widget.RowHeaderPresenter; 30import android.support.v17.leanback.widget.SinglePresenterSelector; 31import android.support.v17.leanback.widget.VerticalGridView; 32import android.support.v7.widget.RecyclerView; 33import android.util.TypedValue; 34import android.view.View; 35import android.view.ViewGroup; 36import android.view.View.OnLayoutChangeListener; 37import android.widget.FrameLayout; 38 39/** 40 * An internal fragment containing a list of row headers. 41 */ 42public class HeadersFragment extends BaseRowFragment { 43 44 interface OnHeaderClickedListener { 45 void onHeaderClicked(); 46 } 47 48 interface OnHeaderViewSelectedListener { 49 void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row); 50 } 51 52 private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener; 53 private OnHeaderClickedListener mOnHeaderClickedListener; 54 private boolean mHeadersEnabled = true; 55 private boolean mHeadersGone = false; 56 private int mBackgroundColor; 57 private boolean mBackgroundColorSet; 58 59 private static final PresenterSelector sHeaderPresenter = new SinglePresenterSelector( 60 new RowHeaderPresenter(R.layout.lb_header)); 61 62 public HeadersFragment() { 63 setPresenterSelector(sHeaderPresenter); 64 } 65 66 public void setOnHeaderClickedListener(OnHeaderClickedListener listener) { 67 mOnHeaderClickedListener = listener; 68 } 69 70 public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) { 71 mOnHeaderViewSelectedListener = listener; 72 } 73 74 @Override 75 VerticalGridView findGridViewFromRoot(View view) { 76 return (VerticalGridView) view.findViewById(R.id.browse_headers); 77 } 78 79 @Override 80 void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder, 81 int position, int subposition) { 82 if (mOnHeaderViewSelectedListener != null) { 83 if (viewHolder != null && position >= 0) { 84 Row row = (Row) getAdapter().get(position); 85 ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder; 86 mOnHeaderViewSelectedListener.onHeaderSelected( 87 (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), row); 88 } else { 89 mOnHeaderViewSelectedListener.onHeaderSelected(null, null); 90 } 91 } 92 } 93 94 private final ItemBridgeAdapter.AdapterListener mAdapterListener = 95 new ItemBridgeAdapter.AdapterListener() { 96 @Override 97 public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { 98 View headerView = viewHolder.getViewHolder().view; 99 headerView.setOnClickListener(new View.OnClickListener() { 100 @Override 101 public void onClick(View v) { 102 if (mOnHeaderClickedListener != null) { 103 mOnHeaderClickedListener.onHeaderClicked(); 104 } 105 } 106 }); 107 headerView.setFocusable(true); 108 headerView.setFocusableInTouchMode(true); 109 if (mWrapper != null) { 110 viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener); 111 } else { 112 headerView.addOnLayoutChangeListener(sLayoutChangeListener); 113 } 114 } 115 116 }; 117 118 private static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() { 119 @Override 120 public void onLayoutChange(View v, int left, int top, int right, int bottom, 121 int oldLeft, int oldTop, int oldRight, int oldBottom) { 122 v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0); 123 v.setPivotY(v.getMeasuredHeight() / 2); 124 } 125 }; 126 127 @Override 128 int getLayoutResourceId() { 129 return R.layout.lb_headers_fragment; 130 } 131 132 @Override 133 public void onViewCreated(View view, Bundle savedInstanceState) { 134 super.onViewCreated(view, savedInstanceState); 135 final VerticalGridView listView = getVerticalGridView(); 136 if (listView == null) { 137 return; 138 } 139 if (getBridgeAdapter() != null) { 140 FocusHighlightHelper.setupHeaderItemFocusHighlight(listView); 141 } 142 if (mBackgroundColorSet) { 143 listView.setBackgroundColor(mBackgroundColor); 144 updateFadingEdgeToBrandColor(mBackgroundColor); 145 } else { 146 Drawable d = listView.getBackground(); 147 if (d instanceof ColorDrawable) { 148 updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor()); 149 } 150 } 151 updateListViewVisibility(); 152 } 153 154 private void updateListViewVisibility() { 155 final VerticalGridView listView = getVerticalGridView(); 156 if (listView != null) { 157 getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); 158 if (!mHeadersGone) { 159 if (mHeadersEnabled) { 160 listView.setChildrenVisibility(View.VISIBLE); 161 } else { 162 listView.setChildrenVisibility(View.INVISIBLE); 163 } 164 } 165 } 166 } 167 168 void setHeadersEnabled(boolean enabled) { 169 mHeadersEnabled = enabled; 170 updateListViewVisibility(); 171 } 172 173 void setHeadersGone(boolean gone) { 174 mHeadersGone = gone; 175 updateListViewVisibility(); 176 } 177 178 static class NoOverlappingFrameLayout extends FrameLayout { 179 180 public NoOverlappingFrameLayout(Context context) { 181 super(context); 182 } 183 184 /** 185 * Avoid creating hardware layer for header dock. 186 */ 187 @Override 188 public boolean hasOverlappingRendering() { 189 return false; 190 } 191 } 192 193 // Wrapper needed because of conflict between RecyclerView's use of alpha 194 // for ADD animations, and RowHeaderPresnter's use of alpha for selected level. 195 private final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { 196 @Override 197 public void wrap(View wrapper, View wrapped) { 198 ((FrameLayout) wrapper).addView(wrapped); 199 } 200 201 @Override 202 public View createWrapper(View root) { 203 return new NoOverlappingFrameLayout(root.getContext()); 204 } 205 }; 206 @Override 207 void updateAdapter() { 208 super.updateAdapter(); 209 ItemBridgeAdapter adapter = getBridgeAdapter(); 210 if (adapter != null) { 211 adapter.setAdapterListener(mAdapterListener); 212 adapter.setWrapper(mWrapper); 213 } 214 if (adapter != null && getVerticalGridView() != null) { 215 FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView()); 216 } 217 } 218 219 void setBackgroundColor(int color) { 220 mBackgroundColor = color; 221 mBackgroundColorSet = true; 222 223 if (getVerticalGridView() != null) { 224 getVerticalGridView().setBackgroundColor(mBackgroundColor); 225 updateFadingEdgeToBrandColor(mBackgroundColor); 226 } 227 } 228 229 private void updateFadingEdgeToBrandColor(int backgroundColor) { 230 View fadingView = getView().findViewById(R.id.fade_out_edge); 231 Drawable background = fadingView.getBackground(); 232 if (background instanceof GradientDrawable) { 233 background.mutate(); 234 ((GradientDrawable) background).setColors( 235 new int[] {Color.TRANSPARENT, backgroundColor}); 236 } 237 } 238 239 @Override 240 void onTransitionStart() { 241 super.onTransitionStart(); 242 if (!mHeadersEnabled) { 243 // When enabling headers fragment, the RowHeaderView gets a focus but 244 // isShown() is still false because its parent is INVSIBILE, accessibility 245 // event is not sent. 246 // Workaround is: prevent focus to a child view during transition and put 247 // focus on it after transition is done. 248 final VerticalGridView listView = getVerticalGridView(); 249 if (listView != null) { 250 listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); 251 if (listView.hasFocus()) { 252 listView.requestFocus(); 253 } 254 } 255 } 256 } 257 258 @Override 259 void onTransitionEnd() { 260 if (mHeadersEnabled) { 261 final VerticalGridView listView = getVerticalGridView(); 262 if (listView != null) { 263 listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 264 if (listView.hasFocus()) { 265 listView.requestFocus(); 266 } 267 } 268 } 269 super.onTransitionEnd(); 270 } 271} 272