RowPresenter.java revision cf94c5fa8ae8edb7e26a623133207415ceeed187
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.view.View;
17import android.view.ViewGroup;
18
19/**
20 * In addition to {@link Presenter} which defines how to render and bind data to row view,
21 * RowPresenter receives calls from upper level(typically a fragment) when:
22 * <ul>
23 * <li>
24 * Row is selected via {@link #setRowViewSelected(ViewHolder, boolean)}.  The event
25 * is triggered immediately when there is a row selection change before the selection
26 * animation is started.
27 * Subclass of RowPresenter may override and add more works in
28 * {@link #onRowViewSelected(ViewHolder, boolean)}.
29 * </li>
30 * <li>
31 * Row is expanded to full width via {@link #setRowViewExpanded(ViewHolder, boolean)}.
32 * The event is triggered immediately before the expand animation is started.
33 * Subclass of RowPresenter may override and add more works in
34 * {@link #onRowViewExpanded(ViewHolder, boolean)}.
35 * </li>
36 * </ul>
37 * </p>
38 * Subclass of RowPresenter may add more fields in ViewHolder by overriding
39 * {@link #createRowViewHolder(ViewGroup)} and {@link #initializeRowViewHolder(ViewHolder)}.
40 * </p>
41 * <p>
42 * RowPresenter contains an optional and replaceable {@link RowHeaderPresenter} that
43 * renders header.  User can disable default rendering or replace with a new header presenter
44 * by calling {@link #setHeaderPresenter(RowHeaderPresenter)}.
45 * </p>
46 * <p>
47 * Provides {@link OnItemSelectedListener} and {@link OnItemClickedListener}.
48 * </p>
49 */
50public abstract class RowPresenter extends Presenter {
51
52    public static class ViewHolder extends Presenter.ViewHolder {
53        RowHeaderPresenter.ViewHolder mHeaderViewHolder;
54        Row mRow;
55        boolean mSelected;
56        boolean mExpanded;
57        boolean mInitialzed;
58        float mSelectLevel = 0f; // initially unselected
59        public ViewHolder(View view) {
60            super(view);
61        }
62        public final Row getRow() {
63            return mRow;
64        }
65        public final boolean isExpanded() {
66            return mExpanded;
67        }
68        public final boolean isSelected() {
69            return mSelected;
70        }
71        public final float getSelectLevel() {
72            return mSelectLevel;
73        }
74        public final RowHeaderPresenter.ViewHolder getHeaderViewHolder() {
75            return mHeaderViewHolder;
76        }
77    }
78
79    private RowHeaderPresenter mHeaderPresenter = new RowHeaderPresenter();
80    private OnItemSelectedListener mOnItemSelectedListener;
81    private OnItemClickedListener mOnItemClickedListener;
82
83    boolean mSelectEffectEnabled = true;
84
85    @Override
86    public final Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
87        ViewHolder vh = createRowViewHolder(parent);
88        vh.mInitialzed = false;
89        initializeRowViewHolder(vh);
90        if (!vh.mInitialzed) {
91            throw new RuntimeException("super.initializeRowViewHolder() must be called");
92        }
93        return vh;
94    }
95
96    /**
97     * Called to create a ViewHolder object for row,  subclass of {@link RowPresenter}
98     * should override and return a different concrete ViewHolder object.
99     */
100    protected abstract ViewHolder createRowViewHolder(ViewGroup parent);
101
102    /**
103     * Called after a {@link RowPresenter.ViewHolder} is created,
104     * subclass of {@link RowPresenter} may override this method and start with calling
105     * super.initializeRowViewHolder(ViewHolder).
106     */
107    protected void initializeRowViewHolder(ViewHolder vh) {
108        if (mHeaderPresenter != null) {
109            vh.mHeaderViewHolder = (RowHeaderPresenter.ViewHolder)
110                    mHeaderPresenter.onCreateViewHolder((ViewGroup) vh.view);
111            ((ViewGroup) vh.view).addView(vh.mHeaderViewHolder.view, 0);
112        }
113        vh.mInitialzed = true;
114    }
115
116    /**
117     * Change the presenter used for rendering header. Can be null to disable header rendering.
118     * The method must be called before creating any row view.
119     */
120    public final void setHeaderPresenter(RowHeaderPresenter headerPresenter) {
121        mHeaderPresenter = headerPresenter;
122    }
123
124    /**
125     * Get optional presenter used for rendering header.  May return null.
126     */
127    public final RowHeaderPresenter getHeaderPresenter() {
128        return mHeaderPresenter;
129    }
130
131    /**
132     * Change expanded state of row view.
133     */
134    public final void setRowViewExpanded(ViewHolder holder, boolean expanded) {
135        holder.mExpanded = expanded;
136        onRowViewExpanded(holder, expanded);
137    }
138
139    /**
140     * Change select state of row view.
141     */
142    public final void setRowViewSelected(ViewHolder holder, boolean selected) {
143        holder.mSelected = selected;
144        onRowViewSelected(holder, selected);
145    }
146
147    /**
148     * Subclass may override and respond to expanded state change of row in fragment.
149     * Default implementation hide/show header view depending on expanded state.
150     * Subclass may make visual changes to row view but not allowed to create
151     * animation on the row view.
152     */
153    protected void onRowViewExpanded(ViewHolder vh, boolean expanded) {
154        if (mHeaderPresenter != null && vh.mHeaderViewHolder != null) {
155            mHeaderPresenter.setHidden(vh.mHeaderViewHolder, !expanded);
156        }
157    }
158
159    /**
160     * Subclass may override and respond to event Row is selected.
161     * Subclass may make visual changes to row view but not allowed to create
162     * animation on the row view.
163     */
164    protected void onRowViewSelected(ViewHolder vh, boolean selected) {
165        if (selected && mOnItemSelectedListener != null) {
166            mOnItemSelectedListener.onItemSelected(null, vh.getRow());
167        }
168    }
169
170    /**
171     * Set current select level from 0(unselected) to 1(selected).
172     * Subclass should override {@link #onSelectLevelChanged(ViewHolder)}.
173     */
174    public final void setSelectLevel(ViewHolder vh, float level) {
175        vh.mSelectLevel = level;
176        onSelectLevelChanged(vh);
177    }
178
179    /**
180     * Get current select level from 0(unselected) to 1(selected).
181     */
182    public final float getSelectLevel(ViewHolder vh) {
183        return vh.mSelectLevel;
184    }
185
186    /**
187     * Callback when select level is changed.  Default implementation applies select level
188     * to {@link RowHeaderPresenter#setSelectLevel(RowHeaderPresenter.ViewHolder, float)}
189     * when {@link #getSelectEffectEnabled()} is true.
190     * Subclass may override this function and implements its own select effect.  When it
191     * overrides,  it should also override {@link #isUsingDefaultSelectEffect()} to disable
192     * the default dimming effect applied by framework.
193     */
194    protected void onSelectLevelChanged(ViewHolder vh) {
195        if (getSelectEffectEnabled() && vh.mHeaderViewHolder != null) {
196            mHeaderPresenter.setSelectLevel(vh.mHeaderViewHolder, vh.mSelectLevel);
197        }
198    }
199
200    /**
201     * Enables or disables the row selection effect.
202     * This is not only for enable/disable default dim implementation but also subclass must
203     * respect this flag.
204     */
205    public final void setSelectEffectEnabled(boolean applyDimOnSelect) {
206        mSelectEffectEnabled = applyDimOnSelect;
207    }
208
209    /**
210     * Returns true if row selection effect is enabled.
211     * This is not only for enable/disable default dim implementation but also subclass must
212     * respect this flag.
213     */
214    public final boolean getSelectEffectEnabled() {
215        return mSelectEffectEnabled;
216    }
217
218    /**
219     * Return if using default dimming effect provided by framework (fragment).  Subclass
220     * may(most likely) return false and override {@link #onSelectLevelChanged(ViewHolder)}.
221     */
222    public boolean isUsingDefaultSelectEffect() {
223        return true;
224    }
225
226    @Override
227    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
228        ViewHolder vh = (ViewHolder) viewHolder;
229        vh.mRow = (Row) item;
230        if (vh.mHeaderViewHolder != null) {
231            mHeaderPresenter.onBindViewHolder(vh.mHeaderViewHolder, item);
232        }
233        vh.mSelected = false;
234        vh.mExpanded = false;
235        onRowViewExpanded(vh, false);
236        onRowViewSelected(vh, false);
237    }
238
239    @Override
240    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
241        ViewHolder vh = (ViewHolder) viewHolder;
242        if (vh.mHeaderViewHolder != null) {
243            mHeaderPresenter.onUnbindViewHolder(vh.mHeaderViewHolder);
244        }
245        vh.mRow = null;
246    }
247
248    @Override
249    public void onViewAttachedToWindow(Presenter.ViewHolder holder) {
250        ViewHolder vh = (ViewHolder) holder;
251        if (vh.mHeaderViewHolder != null) {
252            mHeaderPresenter.onViewAttachedToWindow(vh.mHeaderViewHolder);
253        }
254    }
255
256    @Override
257    public void onViewDetachedFromWindow(Presenter.ViewHolder holder) {
258        ViewHolder vh = (ViewHolder) holder;
259        if (vh.mHeaderViewHolder != null) {
260            mHeaderPresenter.onViewDetachedFromWindow(vh.mHeaderViewHolder);
261        }
262    }
263
264    /**
265     * Set listener for item or row selection.  RowPresenter fires row selection
266     * event with null item, subclass of RowPresenter e.g. {@link ListRowPresenter} can
267     * fire a selection event with selected item.
268     */
269    public final void setOnItemSelectedListener(OnItemSelectedListener listener) {
270        mOnItemSelectedListener = listener;
271    }
272
273    /**
274     * Get listener for item or row selection.
275     */
276    public final OnItemSelectedListener getOnItemSelectedListener() {
277        return mOnItemSelectedListener;
278    }
279
280    /**
281     * Set listener for item click event.  RowPresenter does nothing but subclass of
282     * RowPresenter may fire item click event if it does have a concept of item.
283     * OnItemClickedListener will override the listener that item presenter sets during
284     * {@link Presenter#onCreateViewHolder(ViewGroup)}.  So in general,  developer should
285     * choose one of the listeners but not both.
286     */
287    public final void setOnItemClickedListener(OnItemClickedListener listener) {
288        mOnItemClickedListener = listener;
289    }
290
291    /**
292     * Set listener for item click event.
293     */
294    public final OnItemClickedListener getOnItemClickedListener() {
295        return mOnItemClickedListener;
296    }
297}
298