ControlBarPresenter.java revision cf992de2d34abb8228dc6cb39fffe97346823a37
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 */ 14package android.support.v17.leanback.widget; 15 16import android.content.Context; 17import android.support.v17.leanback.R; 18import android.util.SparseArray; 19import android.view.LayoutInflater; 20import android.view.View; 21import android.view.ViewGroup; 22import android.widget.LinearLayout; 23 24/** 25 * A presenter that assumes a LinearLayout container for a series 26 * of control buttons backed by objects of type {@link Action}. 27 * 28 * Different layouts may be passed to the presenter constructor. 29 * The layout must contain a view with id control_bar. 30 */ 31class ControlBarPresenter extends Presenter { 32 33 private static final int MAX_CONTROLS = 7; 34 35 /** 36 * The data type expected by this presenter. 37 */ 38 static class BoundData { 39 /** 40 * Adapter containing objects of type {@link Action}. 41 */ 42 ObjectAdapter adapter; 43 44 /** 45 * The presenter to be used for the adapter objects. 46 */ 47 Presenter presenter; 48 } 49 50 /** 51 * Listener for control selected events. 52 */ 53 interface OnControlSelectedListener { 54 void onControlSelected(Presenter.ViewHolder controlViewHolder, Object item, 55 BoundData data); 56 } 57 58 /** 59 * Listener for control clicked events. 60 */ 61 interface OnControlClickedListener { 62 void onControlClicked(Presenter.ViewHolder controlViewHolder, Object item, 63 BoundData data); 64 } 65 66 class ViewHolder extends Presenter.ViewHolder { 67 ObjectAdapter mAdapter; 68 BoundData mData; 69 Presenter mPresenter; 70 ControlBar mControlBar; 71 SparseArray<Presenter.ViewHolder> mViewHolders = 72 new SparseArray<Presenter.ViewHolder>(); 73 ObjectAdapter.DataObserver mDataObserver; 74 75 /** 76 * Constructor for the ViewHolder. 77 */ 78 ViewHolder(View rootView) { 79 super(rootView); 80 mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar); 81 if (mControlBar == null) { 82 throw new IllegalStateException("Couldn't find control_bar"); 83 } 84 mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() { 85 @Override 86 public void onChildFocusedListener(View child, View focused) { 87 if (mOnControlSelectedListener == null) { 88 return; 89 } 90 for (int position = 0; position < mViewHolders.size(); position++) { 91 if (mViewHolders.get(position).view == child) { 92 mOnControlSelectedListener.onControlSelected( 93 mViewHolders.get(position), 94 getDisplayedAdapter().get(position), mData); 95 break; 96 } 97 } 98 } 99 }); 100 mDataObserver = new ObjectAdapter.DataObserver() { 101 @Override 102 public void onChanged() { 103 if (mAdapter == getDisplayedAdapter()) { 104 showControls(mPresenter); 105 } 106 } 107 @Override 108 public void onItemRangeChanged(int positionStart, int itemCount) { 109 if (mAdapter == getDisplayedAdapter()) { 110 for (int i = 0; i < itemCount; i++) { 111 bindControlToAction(positionStart + i, mPresenter); 112 } 113 } 114 } 115 }; 116 } 117 118 int getChildMarginFromCenter(Context context, int numControls) { 119 // Includes margin between icons plus two times half the icon width. 120 return getChildMarginDefault(context) + getControlIconWidth(context); 121 } 122 123 void showControls(Presenter presenter) { 124 ObjectAdapter adapter = getDisplayedAdapter(); 125 // Shrink the number of attached views 126 View focusedView = mControlBar.getFocusedChild(); 127 if (focusedView != null && adapter.size() > 0 && 128 mControlBar.indexOfChild(focusedView) >= adapter.size()) { 129 mControlBar.getChildAt(adapter.size() - 1).requestFocus(); 130 } 131 for (int i = mControlBar.getChildCount() - 1; i >= adapter.size(); i--) { 132 mControlBar.removeViewAt(i); 133 } 134 for (int position = 0; position < adapter.size() && position < MAX_CONTROLS; 135 position++) { 136 bindControlToAction(position, adapter, presenter); 137 } 138 mControlBar.setChildMarginFromCenter( 139 getChildMarginFromCenter(mControlBar.getContext(), adapter.size())); 140 } 141 142 void bindControlToAction(int position, Presenter presenter) { 143 bindControlToAction(position, getDisplayedAdapter(), presenter); 144 } 145 146 private void bindControlToAction(final int position, 147 ObjectAdapter adapter, Presenter presenter) { 148 Presenter.ViewHolder vh = mViewHolders.get(position); 149 Object item = adapter.get(position); 150 if (vh == null) { 151 vh = presenter.onCreateViewHolder(mControlBar); 152 mViewHolders.put(position, vh); 153 154 final Presenter.ViewHolder itemViewHolder = vh; 155 presenter.setOnClickListener(vh, new View.OnClickListener() { 156 @Override 157 public void onClick(View v) { 158 Object item = getDisplayedAdapter().get(position); 159 if (mOnControlClickedListener != null) { 160 mOnControlClickedListener.onControlClicked(itemViewHolder, item, 161 mData); 162 } 163 } 164 }); 165 } 166 if (vh.view.getParent() == null) { 167 mControlBar.addView(vh.view); 168 } 169 presenter.onBindViewHolder(vh, item); 170 } 171 172 /** 173 * Returns the adapter currently bound to the displayed controls. 174 * May be overridden in a subclass. 175 */ 176 ObjectAdapter getDisplayedAdapter() { 177 return mAdapter; 178 } 179 } 180 181 private OnControlClickedListener mOnControlClickedListener; 182 private OnControlSelectedListener mOnControlSelectedListener; 183 private int mLayoutResourceId; 184 private static int sChildMarginDefault; 185 private static int sControlIconWidth; 186 187 /** 188 * Constructor for a ControlBarPresenter. 189 * 190 * @param layoutResourceId The resource id of the layout for this presenter. 191 */ 192 public ControlBarPresenter(int layoutResourceId) { 193 mLayoutResourceId = layoutResourceId; 194 } 195 196 /** 197 * Returns the layout resource id. 198 */ 199 public int getLayoutResourceId() { 200 return mLayoutResourceId; 201 } 202 203 /** 204 * Sets the listener for control clicked events. 205 */ 206 public void setOnControlClickedListener(OnControlClickedListener listener) { 207 mOnControlClickedListener = listener; 208 } 209 210 /** 211 * Returns the listener for control clicked events. 212 */ 213 public OnControlClickedListener getOnItemViewClickedListener() { 214 return mOnControlClickedListener; 215 } 216 217 /** 218 * Sets the listener for control selection. 219 */ 220 public void setOnControlSelectedListener(OnControlSelectedListener listener) { 221 mOnControlSelectedListener = listener; 222 } 223 224 /** 225 * Returns the listener for control selection. 226 */ 227 public OnControlSelectedListener getOnItemControlListener() { 228 return mOnControlSelectedListener; 229 } 230 231 @Override 232 public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { 233 View v = LayoutInflater.from(parent.getContext()) 234 .inflate(getLayoutResourceId(), parent, false); 235 return new ViewHolder(v); 236 } 237 238 @Override 239 public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { 240 ViewHolder vh = (ViewHolder) holder; 241 BoundData data = (BoundData) item; 242 if (vh.mAdapter != data.adapter) { 243 vh.mAdapter = data.adapter; 244 vh.mAdapter.registerObserver(vh.mDataObserver); 245 } 246 vh.mPresenter = data.presenter; 247 vh.mData = data; 248 vh.showControls(vh.mPresenter); 249 } 250 251 @Override 252 public void onUnbindViewHolder(Presenter.ViewHolder holder) { 253 ViewHolder vh = (ViewHolder) holder; 254 vh.mAdapter.unregisterObserver(vh.mDataObserver); 255 vh.mAdapter = null; 256 vh.mData = null; 257 } 258 259 int getChildMarginDefault(Context context) { 260 if (sChildMarginDefault == 0) { 261 sChildMarginDefault = context.getResources().getDimensionPixelSize( 262 R.dimen.lb_playback_controls_child_margin_default); 263 } 264 return sChildMarginDefault; 265 } 266 267 int getControlIconWidth(Context context) { 268 if (sControlIconWidth == 0) { 269 sControlIconWidth = context.getResources().getDimensionPixelSize( 270 R.dimen.lb_control_icon_width); 271 } 272 return sControlIconWidth; 273 } 274} 275