ItemBridgeAdapter.java revision 86a6309c3e89ec6abc40ec045bfaef7827cbe427
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 implements FacetProviderAdapter {
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 implements FacetProvider {
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        @Override
134        public Object getFacet(Class<?> facetClass) {
135            return mHolder.getFacet(facetClass);
136        }
137
138        ViewHolder(Presenter presenter, View view, Presenter.ViewHolder holder) {
139            super(view);
140            mPresenter = presenter;
141            mHolder = holder;
142        }
143    }
144
145    private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
146        @Override
147        public void onChanged() {
148            ItemBridgeAdapter.this.notifyDataSetChanged();
149        }
150        @Override
151        public void onItemRangeChanged(int positionStart, int itemCount) {
152            ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
153        }
154        @Override
155        public void onItemRangeInserted(int positionStart, int itemCount) {
156            ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
157        }
158        @Override
159        public void onItemRangeRemoved(int positionStart, int itemCount) {
160            ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
161        }
162    };
163
164    public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
165        setAdapter(adapter);
166        mPresenterSelector = presenterSelector;
167    }
168
169    public ItemBridgeAdapter(ObjectAdapter adapter) {
170        this(adapter, null);
171    }
172
173    public ItemBridgeAdapter() {
174    }
175
176    /**
177     * Sets the {@link ObjectAdapter}.
178     */
179    public void setAdapter(ObjectAdapter adapter) {
180        if (mAdapter != null) {
181            mAdapter.unregisterObserver(mDataObserver);
182        }
183        mAdapter = adapter;
184        if (mAdapter == null) {
185            return;
186        }
187
188        mAdapter.registerObserver(mDataObserver);
189        if (hasStableIds() != mAdapter.hasStableIds()) {
190            setHasStableIds(mAdapter.hasStableIds());
191        }
192    }
193
194    /**
195     * Sets the {@link Wrapper}.
196     */
197    public void setWrapper(Wrapper wrapper) {
198        mWrapper = wrapper;
199    }
200
201    /**
202     * Returns the {@link Wrapper}.
203     */
204    public Wrapper getWrapper() {
205        return mWrapper;
206    }
207
208    void setFocusHighlight(FocusHighlightHandler listener) {
209        mFocusHighlight = listener;
210        if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
211    }
212
213    /**
214     * Clears the adapter.
215     */
216    public void clear() {
217        setAdapter(null);
218    }
219
220    /**
221     * Sets the presenter mapper array.
222     */
223    public void setPresenterMapper(ArrayList<Presenter> presenters) {
224        mPresenters = presenters;
225    }
226
227    /**
228     * Returns the presenter mapper array.
229     */
230    public ArrayList<Presenter> getPresenterMapper() {
231        return mPresenters;
232    }
233
234    @Override
235    public int getItemCount() {
236        return mAdapter.size();
237    }
238
239    @Override
240    public int getItemViewType(int position) {
241        PresenterSelector presenterSelector = mPresenterSelector != null ?
242                mPresenterSelector : mAdapter.getPresenterSelector();
243        Object item = mAdapter.get(position);
244        Presenter presenter = presenterSelector.getPresenter(item);
245        int type = mPresenters.indexOf(presenter);
246        if (type < 0) {
247            mPresenters.add(presenter);
248            type = mPresenters.indexOf(presenter);
249            if (DEBUG) Log.v(TAG, "getItemViewType added presenter " + presenter + " type " + type);
250            onAddPresenter(presenter, type);
251            if (mAdapterListener != null) {
252                mAdapterListener.onAddPresenter(presenter, type);
253            }
254        }
255        return type;
256    }
257
258    /**
259     * Called when presenter is added to Adapter.
260     */
261    protected void onAddPresenter(Presenter presenter, int type) {
262    }
263
264    /**
265     * Called when ViewHolder is created.
266     */
267    protected void onCreate(ViewHolder viewHolder) {
268    }
269
270    /**
271     * Called when ViewHolder has been bound to data.
272     */
273    protected void onBind(ViewHolder viewHolder) {
274    }
275
276    /**
277     * Called when ViewHolder has been unbound from data.
278     */
279    protected void onUnbind(ViewHolder viewHolder) {
280    }
281
282    /**
283     * Called when ViewHolder has been attached to window.
284     */
285    protected void onAttachedToWindow(ViewHolder viewHolder) {
286    }
287
288    /**
289     * Called when ViewHolder has been detached from window.
290     */
291    protected void onDetachedFromWindow(ViewHolder viewHolder) {
292    }
293
294    /**
295     * {@link View.OnFocusChangeListener} that assigned in
296     * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
297     * {@link View.OnFocusChangeListener} after that.
298     */
299    @Override
300    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
301        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
302        Presenter presenter = mPresenters.get(viewType);
303        Presenter.ViewHolder presenterVh;
304        View view;
305        if (mWrapper != null) {
306            view = mWrapper.createWrapper(parent);
307            presenterVh = presenter.onCreateViewHolder(parent);
308            mWrapper.wrap(view, presenterVh.view);
309        } else {
310            presenterVh = presenter.onCreateViewHolder(parent);
311            view = presenterVh.view;
312        }
313        ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
314        onCreate(viewHolder);
315        if (mAdapterListener != null) {
316            mAdapterListener.onCreate(viewHolder);
317        }
318        View presenterView = viewHolder.mHolder.view;
319        if (presenterView != null) {
320            viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener();
321            presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
322        }
323        if (mFocusHighlight != null) {
324            mFocusHighlight.onInitializeView(view);
325        }
326        return viewHolder;
327    }
328
329    /**
330     * Sets the AdapterListener.
331     */
332    public void setAdapterListener(AdapterListener listener) {
333        mAdapterListener = listener;
334    }
335
336    @Override
337    public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
338        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
339        ViewHolder viewHolder = (ViewHolder) holder;
340        viewHolder.mItem = mAdapter.get(position);
341
342        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
343
344        onBind(viewHolder);
345        if (mAdapterListener != null) {
346            mAdapterListener.onBind(viewHolder);
347        }
348    }
349
350    @Override
351    public final void onViewRecycled(RecyclerView.ViewHolder holder) {
352        ViewHolder viewHolder = (ViewHolder) holder;
353        viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
354        onUnbind(viewHolder);
355        if (mAdapterListener != null) {
356            mAdapterListener.onUnbind(viewHolder);
357        }
358        viewHolder.mItem = null;
359    }
360
361    @Override
362    public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
363        ViewHolder viewHolder = (ViewHolder) holder;
364        onAttachedToWindow(viewHolder);
365        if (mAdapterListener != null) {
366            mAdapterListener.onAttachedToWindow(viewHolder);
367        }
368        viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
369    }
370
371    @Override
372    public final void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
373        ViewHolder viewHolder = (ViewHolder) holder;
374        viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
375        onDetachedFromWindow(viewHolder);
376        if (mAdapterListener != null) {
377            mAdapterListener.onDetachedFromWindow(viewHolder);
378        }
379    }
380
381    @Override
382    public long getItemId(int position) {
383        return mAdapter.getId(position);
384    }
385
386    @Override
387    public FacetProvider getFacetProvider(int type) {
388        return mPresenters.get(type);
389    }
390}
391