ControlBarPresenter.java revision aa67105babce5fb14e1f39b57d4c84ce634afa62
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 * A ViewHolder for an actions bar. 52 */ 53 class ViewHolder extends Presenter.ViewHolder { 54 ObjectAdapter mAdapter; 55 Presenter mPresenter; 56 ControlBar mControlBar; 57 SparseArray<Presenter.ViewHolder> mViewHolders = 58 new SparseArray<Presenter.ViewHolder>(); 59 ObjectAdapter.DataObserver mDataObserver; 60 61 /** 62 * Constructor for the ViewHolder. 63 */ 64 ViewHolder(View rootView) { 65 super(rootView); 66 mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar); 67 if (mControlBar == null) { 68 throw new IllegalStateException("Couldn't find control_bar"); 69 } 70 mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() { 71 @Override 72 public void onChildFocusedListener(View child, View focused) { 73 if (mOnItemViewSelectedListener == null) { 74 return; 75 } 76 for (int position = 0; position < mViewHolders.size(); position++) { 77 if (mViewHolders.get(position).view == child) { 78 mOnItemViewSelectedListener.onItemSelected( 79 mViewHolders.get(position), getDisplayedAdapter().get(position), 80 null, null); 81 break; 82 } 83 } 84 } 85 }); 86 mDataObserver = new ObjectAdapter.DataObserver() { 87 @Override 88 public void onChanged() { 89 if (mAdapter == getDisplayedAdapter()) { 90 showControls(mPresenter); 91 } 92 } 93 @Override 94 public void onItemRangeChanged(int positionStart, int itemCount) { 95 if (mAdapter == getDisplayedAdapter()) { 96 for (int i = 0; i < itemCount; i++) { 97 bindControlToAction(positionStart + i, mPresenter); 98 } 99 } 100 } 101 }; 102 } 103 104 int getChildMarginFromCenter(Context context, int numControls) { 105 // Includes margin between icons plus two times half the icon width. 106 return getChildMarginDefault(context) + getControlIconWidth(context); 107 } 108 109 void showControls(Presenter presenter) { 110 View focusedChild = mControlBar.getFocusedChild(); 111 ObjectAdapter adapter = getDisplayedAdapter(); 112 mControlBar.removeAllViews(); 113 for (int position = 0; position < adapter.size() && position < MAX_CONTROLS; 114 position++) { 115 bindControlToAction(position, adapter, presenter); 116 } 117 if (focusedChild != null) { 118 focusedChild.requestFocus(); 119 } 120 mControlBar.setChildMarginFromCenter( 121 getChildMarginFromCenter(mControlBar.getContext(), adapter.size())); 122 } 123 124 void bindControlToAction(int position, Presenter presenter) { 125 bindControlToAction(position, getDisplayedAdapter(), presenter); 126 } 127 128 private void bindControlToAction(final int position, 129 ObjectAdapter adapter, Presenter presenter) { 130 Presenter.ViewHolder vh = mViewHolders.get(position); 131 Object item = adapter.get(position); 132 if (vh == null) { 133 vh = presenter.onCreateViewHolder(mControlBar); 134 mViewHolders.put(position, vh); 135 presenter.setOnClickListener(vh, new View.OnClickListener() { 136 @Override 137 public void onClick(View v) { 138 Object item = getDisplayedAdapter().get(position); 139 if (mOnActionClickedListener != null && item instanceof Action) { 140 mOnActionClickedListener.onActionClicked((Action) item); 141 } 142 } 143 }); 144 } 145 if (vh.view.getParent() == null) { 146 mControlBar.addView(vh.view); 147 } 148 presenter.onBindViewHolder(vh, item); 149 } 150 151 /** 152 * Returns the adapter currently bound to the displayed controls. 153 * May be overridden in a subclass. 154 */ 155 ObjectAdapter getDisplayedAdapter() { 156 return mAdapter; 157 } 158 } 159 160 private OnActionClickedListener mOnActionClickedListener; 161 private OnItemViewSelectedListener mOnItemViewSelectedListener; 162 private int mLayoutResourceId; 163 private static int sChildMarginDefault; 164 private static int sControlIconWidth; 165 166 /** 167 * Constructor for a ControlBarPresenter. 168 * 169 * @param layoutResourceId The resource id of the layout for this presenter. 170 */ 171 public ControlBarPresenter(int layoutResourceId) { 172 mLayoutResourceId = layoutResourceId; 173 } 174 175 /** 176 * Returns the layout resource id. 177 */ 178 public int getLayoutResourceId() { 179 return mLayoutResourceId; 180 } 181 182 /** 183 * Sets the listener for {@link Action} click events. 184 */ 185 public void setOnActionClickedListener(OnActionClickedListener listener) { 186 mOnActionClickedListener = listener; 187 } 188 189 /** 190 * Gets the listener for {@link Action} click events. 191 */ 192 public OnActionClickedListener getOnActionClickedListener() { 193 return mOnActionClickedListener; 194 } 195 196 /** 197 * Sets the listener for item selection. When this listener is invoked, 198 * the rowViewHolder and row are always null. 199 */ 200 public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { 201 mOnItemViewSelectedListener = listener; 202 } 203 204 /** 205 * Gets the listener for item selection. 206 */ 207 public OnItemViewSelectedListener getOnItemViewSelectedListener() { 208 return mOnItemViewSelectedListener; 209 } 210 211 @Override 212 public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { 213 View v = LayoutInflater.from(parent.getContext()) 214 .inflate(getLayoutResourceId(), parent, false); 215 return new ViewHolder(v); 216 } 217 218 @Override 219 public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { 220 ViewHolder vh = (ViewHolder) holder; 221 BoundData data = (BoundData) item; 222 if (vh.mAdapter != data.adapter) { 223 vh.mAdapter = data.adapter; 224 vh.mAdapter.registerObserver(vh.mDataObserver); 225 } 226 vh.mPresenter = data.presenter; 227 vh.showControls(vh.mPresenter); 228 } 229 230 @Override 231 public void onUnbindViewHolder(Presenter.ViewHolder holder) { 232 ViewHolder vh = (ViewHolder) holder; 233 vh.mAdapter.unregisterObserver(vh.mDataObserver); 234 vh.mAdapter = null; 235 } 236 237 int getChildMarginDefault(Context context) { 238 if (sChildMarginDefault == 0) { 239 sChildMarginDefault = context.getResources().getDimensionPixelSize( 240 R.dimen.lb_playback_controls_child_margin_default); 241 } 242 return sChildMarginDefault; 243 } 244 245 int getControlIconWidth(Context context) { 246 if (sControlIconWidth == 0) { 247 sControlIconWidth = context.getResources().getDimensionPixelSize( 248 R.dimen.lb_control_icon_width); 249 } 250 return sControlIconWidth; 251 } 252} 253