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