HeadersFragment.java revision 3659dc62f9e55b1043edb4105c311c8ef997f2ae
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 if (mBackgroundColorSet) { 135 updateFadingEdgeToBrandColor(); 136 } 137 updateListViewVisibility(); 138 } 139 140 private void updateListViewVisibility() { 141 final VerticalGridView listView = getVerticalGridView(); 142 if (listView != null) { 143 getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); 144 if (!mHeadersGone) { 145 if (mHeadersEnabled) { 146 listView.setChildrenVisibility(View.VISIBLE); 147 } else { 148 listView.setChildrenVisibility(View.INVISIBLE); 149 } 150 } 151 } 152 } 153 154 void setHeadersEnabled(boolean enabled) { 155 mHeadersEnabled = enabled; 156 updateListViewVisibility(); 157 } 158 159 void setHeadersGone(boolean gone) { 160 mHeadersGone = gone; 161 updateListViewVisibility(); 162 } 163 164 static class NoOverlappingFrameLayout extends FrameLayout { 165 166 public NoOverlappingFrameLayout(Context context) { 167 super(context); 168 } 169 170 /** 171 * Avoid creating hardware layer for header dock. 172 */ 173 @Override 174 public boolean hasOverlappingRendering() { 175 return false; 176 } 177 } 178 179 // Wrapper needed because of conflict between RecyclerView's use of alpha 180 // for ADD animations, and RowHeaderPresnter's use of alpha for selected level. 181 private final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { 182 @Override 183 public void wrap(View wrapper, View wrapped) { 184 ((FrameLayout) wrapper).addView(wrapped); 185 } 186 187 @Override 188 public View createWrapper(View root) { 189 return new NoOverlappingFrameLayout(root.getContext()); 190 } 191 }; 192 @Override 193 protected void updateAdapter() { 194 super.updateAdapter(); 195 ItemBridgeAdapter adapter = getBridgeAdapter(); 196 if (adapter != null) { 197 adapter.setAdapterListener(mAdapterListener); 198 adapter.setWrapper(mWrapper); 199 } 200 if (adapter != null && getVerticalGridView() != null) { 201 FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView()); 202 } 203 } 204 205 void setBackgroundColor(int color) { 206 mBackgroundColor = color; 207 mBackgroundColorSet = true; 208 209 if (getView() != null) { 210 getView().setBackgroundColor(mBackgroundColor); 211 updateFadingEdgeToBrandColor(); 212 } 213 } 214 215 private void updateFadingEdgeToBrandColor() { 216 View fadingView = getView().findViewById(R.id.fade_out_edge); 217 Drawable background = fadingView.getBackground(); 218 if (background instanceof GradientDrawable) { 219 background.mutate(); 220 ((GradientDrawable) background).setColors( 221 new int[]{Color.TRANSPARENT,mBackgroundColor}); 222 } 223 } 224 225 int getBackgroundColor() { 226 if (getActivity() == null) { 227 throw new IllegalStateException("Activity must be attached"); 228 } 229 230 if (mBackgroundColorSet) { 231 return mBackgroundColor; 232 } 233 234 TypedValue outValue = new TypedValue(); 235 getActivity().getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true); 236 return getResources().getColor(outValue.resourceId); 237 } 238} 239