ItemBridgeAdapter.java revision bc0edc3ab9bac3c8d7d3cc9de1cb499ea3b4155e
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.support.v7.widget.RecyclerView;
17import android.util.Log;
18import android.view.View;
19import android.view.ViewGroup;
20
21import java.util.ArrayList;
22
23/**
24 * Bridge from {@link Presenter} to {@link RecyclerView.Adapter}. Public to allow use by third
25 * party Presenters.
26 */
27public class ItemBridgeAdapter extends RecyclerView.Adapter {
28    private static final String TAG = "ItemBridgeAdapter";
29    private static final boolean DEBUG = false;
30
31    /**
32     * Interface for listening to ViewHolder operations.
33     */
34    public static class AdapterListener {
35        public void onAddPresenter(Presenter presenter, int type) {
36        }
37        public void onCreate(ViewHolder viewHolder) {
38        }
39        public void onBind(ViewHolder viewHolder) {
40        }
41        public void onUnbind(ViewHolder viewHolder) {
42        }
43        public void onAttachedToWindow(ViewHolder viewHolder) {
44        }
45        public void onDetachedFromWindow(ViewHolder viewHolder) {
46        }
47    }
48
49    /**
50     * Interface for wrapping a view created by a Presenter into another view.
51     * The wrapper must be the immediate parent of the wrapped view.
52     */
53    public static abstract class Wrapper {
54        public abstract View createWrapper(View root);
55        public abstract void wrap(View wrapper, View wrapped);
56    }
57
58    private ObjectAdapter mAdapter;
59    private Wrapper mWrapper;
60    private PresenterSelector mPresenterSelector;
61    private FocusHighlightHandler mFocusHighlight;
62    private AdapterListener mAdapterListener;
63    private ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
64
65    final class OnFocusChangeListener implements View.OnFocusChangeListener {
66        View.OnFocusChangeListener mChainedListener;
67
68        @Override
69        public void onFocusChange(View view, boolean hasFocus) {
70            if (DEBUG) Log.v(TAG, "onFocusChange " + hasFocus + " " + view
71                    + " mFocusHighlight" + mFocusHighlight);
72            if (mWrapper != null) {
73                view = (View) view.getParent();
74            }
75            if (mFocusHighlight != null) {
76                mFocusHighlight.onItemFocused(view, hasFocus);
77            }
78            if (mChainedListener != null) {
79                mChainedListener.onFocusChange(view, hasFocus);
80            }
81        }
82    }
83
84    /**
85     * ViewHolder for the ItemBridgeAdapter.
86     */
87    public class ViewHolder extends RecyclerView.ViewHolder {
88        final Presenter mPresenter;
89        final Presenter.ViewHolder mHolder;
90        final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener();
91        Object mItem;
92        Object mExtraObject;
93
94        /**
95         * Get {@link Presenter}.
96         */
97        public final Presenter getPresenter() {
98            return mPresenter;
99        }
100
101        /**
102         * Get {@link Presenter.ViewHolder}.
103         */
104        public final Presenter.ViewHolder getViewHolder() {
105            return mHolder;
106        }
107
108        /**
109         * Get currently bound object.
110         */
111        public final Object getItem() {
112            return mItem;
113        }
114
115        /**
116         * Get extra object associated with the view.  Developer can attach
117         * any customized UI object in addition to {@link Presenter.ViewHolder}.
118         * A typical use case is attaching an animator object.
119         */
120        public final Object getExtraObject() {
121            return mExtraObject;
122        }
123
124        /**
125         * Set extra object associated with the view.  Developer can attach
126         * any customized UI object in addition to {@link Presenter.ViewHolder}.
127         * A typical use case is attaching an animator object.
128         */
129        public void setExtraObject(Object object) {
130            mExtraObject = object;
131        }
132
133        ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder) {
134            super(view);
135            mPresenter = presenter;
136            mHolder = holder;
137        }
138    }
139
140    private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
141        @Override
142        public void onChanged() {
143            ItemBridgeAdapter.this.notifyDataSetChanged();
144        }
145        @Override
146        public void onItemRangeChanged(int positionStart, int itemCount) {
147            ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
148        }
149        @Override
150        public void onItemRangeInserted(int positionStart, int itemCount) {
151            ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
152        }
153        @Override
154        public void onItemRangeRemoved(int positionStart, int itemCount) {
155            ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
156        }
157    };
158
159    public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
160        setAdapter(adapter);
161        mPresenterSelector = presenterSelector;
162    }
163
164    public ItemBridgeAdapter(ObjectAdapter adapter) {
165        this(adapter, null);
166    }
167
168    public ItemBridgeAdapter() {
169    }
170
171    /**
172     * Sets the {@link ObjectAdapter}.
173     */
174    public void setAdapter(ObjectAdapter adapter) {
175        if (mAdapter != null) {
176            mAdapter.unregisterObserver(mDataObserver);
177        }
178        mAdapter = adapter;
179        if (mAdapter == null) {
180            return;
181        }
182
183        mAdapter.registerObserver(mDataObserver);
184        if (hasStableIds() != mAdapter.hasStableIds()) {
185            setHasStableIds(mAdapter.hasStableIds());
186        }
187    }
188
189    /**
190     * Sets the {@link Wrapper}.
191     */
192    public void setWrapper(Wrapper wrapper) {
193        mWrapper = wrapper;
194    }
195
196    /**
197     * Returns the {@link Wrapper}.
198     */
199    public Wrapper getWrapper() {
200        return mWrapper;
201    }
202
203    void setFocusHighlight(FocusHighlightHandler listener) {
204        mFocusHighlight = listener;
205        if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
206    }
207
208    /**
209     * Clears the adapter.
210     */
211    public void clear() {
212        setAdapter(null);
213    }
214
215    /**
216     * Sets the presenter mapper array.
217     */
218    public void setPresenterMapper(ArrayList<Presenter> presenters) {
219        mPresenters = presenters;
220    }
221
222    /**
223     * Returns the presenter mapper array.
224     */
225    public ArrayList<Presenter> getPresenterMapper() {
226        return mPresenters;
227    }
228
229    @Override
230    public int getItemCount() {
231        return mAdapter.size();
232    }
233
234    @Override
235    public int getItemViewType(int position) {
236        PresenterSelector presenterSelector = mPresenterSelector != null ?
237                mPresenterSelector : mAdapter.getPresenterSelector();
238        Object item = mAdapter.get(position);
239        Presenter presenter = presenterSelector.getPresenter(item);
240        int type = mPresenters.indexOf(presenter);
241        if (type < 0) {
242            mPresenters.add(presenter);
243            type = mPresenters.indexOf(presenter);
244            if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
245            onAddPresenter(presenter, type);
246            if (mAdapterListener != null) {
247                mAdapterListener.onAddPresenter(presenter, type);
248            }
249        }
250        return type;
251    }
252
253    /**
254     * Called when presenter is added to Adapter.
255     */
256    protected void onAddPresenter(Presenter presenter, int type) {
257    }
258
259    /**
260     * Called when ViewHolder is created.
261     */
262    protected void onCreate(ViewHolder viewHolder) {
263    }
264
265    /**
266     * Called when ViewHolder has been bound to data.
267     */
268    protected void onBind(ViewHolder viewHolder) {
269    }
270
271    /**
272     * Called when ViewHolder has been unbound from data.
273     */
274    protected void onUnbind(ViewHolder viewHolder) {
275    }
276
277    /**
278     * Called when ViewHolder has been attached to window.
279     */
280    protected void onAttachedToWindow(ViewHolder viewHolder) {
281    }
282
283    /**
284     * Called when ViewHolder has been detached from window.
285     */
286    protected void onDetachedFromWindow(ViewHolder viewHolder) {
287    }
288
289    /**
290     * {@link View.OnFocusChangeListener} that assigned in
291     * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
292     * {@link View.OnFocusChangeListener} after that.
293     */
294    @Override
295    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
296        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
297        Presenter presenter = mPresenters.get(viewType);
298        Presenter.ViewHolder presenterVh;
299        View view;
300        if (mWrapper != null) {
301            view = mWrapper.createWrapper(parent);
302            presenterVh = presenter.onCreateViewHolder(parent);
303            mWrapper.wrap(view, presenterVh.view);
304        } else {
305            presenterVh = presenter.onCreateViewHolder(parent);
306            view = presenterVh.view;
307        }
308        ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
309        onCreate(viewHolder);
310        if (mAdapterListener != null) {
311            mAdapterListener.onCreate(viewHolder);
312        }
313        View presenterView = viewHolder.mHolder.view;
314        if (presenterView != null) {
315            viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener();
316            presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
317        }
318        if (mFocusHighlight != null) {
319            mFocusHighlight.onInitializeView(view);
320        }
321        return viewHolder;
322    }
323
324    /**
325     * Sets the AdapterListener.
326     */
327    public void setAdapterListener(AdapterListener listener) {
328        mAdapterListener = listener;
329    }
330
331    @Override
332    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
333        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
334        ViewHolder viewHolder = (ViewHolder) holder;
335        viewHolder.mItem = mAdapter.get(position);
336
337        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
338
339        onBind(viewHolder);
340        if (mAdapterListener != null) {
341            mAdapterListener.onBind(viewHolder);
342        }
343    }
344
345    @Override
346    public final void onViewRecycled(RecyclerView.ViewHolder holder) {
347        ViewHolder viewHolder = (ViewHolder) holder;
348        viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
349        onUnbind(viewHolder);
350        if (mAdapterListener != null) {
351            mAdapterListener.onUnbind(viewHolder);
352        }
353        viewHolder.mItem = null;
354    }
355
356    @Override
357    public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
358        ViewHolder viewHolder = (ViewHolder) holder;
359        onAttachedToWindow(viewHolder);
360        if (mAdapterListener != null) {
361            mAdapterListener.onAttachedToWindow(viewHolder);
362        }
363        viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
364    }
365
366    @Override
367    public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
368        ViewHolder viewHolder = (ViewHolder) holder;
369        viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
370        onDetachedFromWindow(viewHolder);
371        if (mAdapterListener != null) {
372            mAdapterListener.onDetachedFromWindow(viewHolder);
373        }
374    }
375
376    @Override
377    public long getItemId(int position) {
378        return mAdapter.getId(position);
379    }
380
381}
382