HeadersFragment.java revision b74149016854eb172c0ef121ccfef749c6ef7eb1
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.Drawable; 20import android.graphics.drawable.GradientDrawable; 21import android.os.Bundle; 22import android.support.v17.leanback.R; 23import android.support.v17.leanback.widget.FocusHighlightHelper; 24import android.support.v17.leanback.widget.ItemBridgeAdapter; 25import android.support.v17.leanback.widget.PresenterSelector; 26import android.support.v17.leanback.widget.OnItemSelectedListener; 27import android.support.v17.leanback.widget.Row; 28import android.support.v17.leanback.widget.RowHeaderPresenter; 29import android.support.v17.leanback.widget.SinglePresenterSelector; 30import android.support.v17.leanback.widget.VerticalGridView; 31import android.util.TypedValue; 32import android.view.View; 33import android.view.ViewGroup; 34import android.view.View.OnLayoutChangeListener; 35import android.widget.FrameLayout; 36 37/** 38 * An internal fragment containing a list of row headers. 39 */ 40public class HeadersFragment extends BaseRowFragment { 41 42 interface OnHeaderClickedListener { 43 void onHeaderClicked(); 44 } 45 46 private OnItemSelectedListener mOnItemSelectedListener; 47 private OnHeaderClickedListener mOnHeaderClickedListener; 48 private boolean mHeadersEnabled = true; 49 private boolean mHeadersGone = false; 50 private int mBackgroundColor; 51 private boolean mBackgroundColorSet; 52 53 private static final PresenterSelector sHeaderPresenter = new SinglePresenterSelector( 54 new RowHeaderPresenter(R.layout.lb_header)); 55 56 public HeadersFragment() { 57 setPresenterSelector(sHeaderPresenter); 58 } 59 60 public void setOnHeaderClickedListener(OnHeaderClickedListener listener) { 61 mOnHeaderClickedListener = listener; 62 } 63 64 public void setOnItemSelectedListener(OnItemSelectedListener listener) { 65 mOnItemSelectedListener = listener; 66 } 67 68 @Override 69 protected VerticalGridView findGridViewFromRoot(View view) { 70 return (VerticalGridView) view.findViewById(R.id.browse_headers); 71 } 72 73 @Override 74 protected void onRowSelected(ViewGroup parent, View view, int position, long id) { 75 if (mOnItemSelectedListener != null) { 76 if (position >= 0) { 77 Row row = (Row) getAdapter().get(position); 78 mOnItemSelectedListener.onItemSelected(null, row); 79 } else { 80 mOnItemSelectedListener.onItemSelected(null, null); 81 } 82 } 83 } 84 85 private final ItemBridgeAdapter.AdapterListener mAdapterListener = 86 new ItemBridgeAdapter.AdapterListener() { 87 @Override 88 public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) { 89 View headerView = viewHolder.getViewHolder().view; 90 headerView.setOnClickListener(new View.OnClickListener() { 91 @Override 92 public void onClick(View v) { 93 if (mOnHeaderClickedListener != null) { 94 mOnHeaderClickedListener.onHeaderClicked(); 95 } 96 } 97 }); 98 headerView.setFocusable(true); 99 headerView.setFocusableInTouchMode(true); 100 if (mWrapper != null) { 101 viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener); 102 } else { 103 headerView.addOnLayoutChangeListener(sLayoutChangeListener); 104 } 105 } 106 107 }; 108 109 private static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() { 110 @Override 111 public void onLayoutChange(View v, int left, int top, int right, int bottom, 112 int oldLeft, int oldTop, int oldRight, int oldBottom) { 113 v.setPivotX(0); 114 v.setPivotY(v.getMeasuredHeight() / 2); 115 } 116 }; 117 118 @Override 119 protected int getLayoutResourceId() { 120 return R.layout.lb_headers_fragment; 121 } 122 123 @Override 124 public void onViewCreated(View view, Bundle savedInstanceState) { 125 super.onViewCreated(view, savedInstanceState); 126 final VerticalGridView listView = getVerticalGridView(); 127 if (listView == null) { 128 return; 129 } 130 if (getBridgeAdapter() != null) { 131 FocusHighlightHelper.setupHeaderItemFocusHighlight(listView); 132 } 133 view.setBackgroundColor(getBackgroundColor()); 134 updateFadingEdgeToBrandColor(getBackgroundColor()); 135 updateListViewVisibility(); 136 } 137 138 private void updateListViewVisibility() { 139 final VerticalGridView listView = getVerticalGridView(); 140 if (listView != null) { 141 getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); 142 if (!mHeadersGone) { 143 if (mHeadersEnabled) { 144 listView.setChildrenVisibility(View.VISIBLE); 145 } else { 146 listView.setChildrenVisibility(View.INVISIBLE); 147 } 148 } 149 } 150 } 151 152 void setHeadersEnabled(boolean enabled) { 153 mHeadersEnabled = enabled; 154 updateListViewVisibility(); 155 } 156 157 void setHeadersGone(boolean gone) { 158 mHeadersGone = gone; 159 updateListViewVisibility(); 160 } 161 162 static class NoOverlappingFrameLayout extends FrameLayout { 163 164 public NoOverlappingFrameLayout(Context context) { 165 super(context); 166 } 167 168 /** 169 * Avoid creating hardware layer for header dock. 170 */ 171 @Override 172 public boolean hasOverlappingRendering() { 173 return false; 174 } 175 } 176 177 // Wrapper needed because of conflict between RecyclerView's use of alpha 178 // for ADD animations, and RowHeaderPresnter's use of alpha for selected level. 179 private final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { 180 @Override 181 public void wrap(View wrapper, View wrapped) { 182 ((FrameLayout) wrapper).addView(wrapped); 183 } 184 185 @Override 186 public View createWrapper(View root) { 187 return new NoOverlappingFrameLayout(root.getContext()); 188 } 189 }; 190 @Override 191 protected void updateAdapter() { 192 super.updateAdapter(); 193 ItemBridgeAdapter adapter = getBridgeAdapter(); 194 if (adapter != null) { 195 adapter.setAdapterListener(mAdapterListener); 196 adapter.setWrapper(mWrapper); 197 } 198 if (adapter != null && getVerticalGridView() != null) { 199 FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView()); 200 } 201 } 202 203 void setBackgroundColor(int color) { 204 mBackgroundColor = color; 205 mBackgroundColorSet = true; 206 207 if (getView() != null) { 208 getView().setBackgroundColor(mBackgroundColor); 209 updateFadingEdgeToBrandColor(mBackgroundColor); 210 } 211 } 212 213 private void updateFadingEdgeToBrandColor(int backgroundColor) { 214 View fadingView = getView().findViewById(R.id.fade_out_edge); 215 Drawable background = fadingView.getBackground(); 216 if (background instanceof GradientDrawable) { 217 background.mutate(); 218 ((GradientDrawable) background).setColors( 219 new int[] {Color.TRANSPARENT, backgroundColor}); 220 } 221 } 222 223 int getBackgroundColor() { 224 if (getActivity() == null) { 225 throw new IllegalStateException("Activity must be attached"); 226 } 227 228 if (mBackgroundColorSet) { 229 return mBackgroundColor; 230 } 231 232 TypedValue outValue = new TypedValue(); 233 getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true); 234 return getResources().getColor(outValue.resourceId); 235 } 236} 237