ItemBridgeAdapter.java revision 739e3805bf2785e6773aede5e2e1643f537305f9
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) {
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    @Override
202    public int getItemCount() {
203        return mAdapter.size();
204    }
205
206    @Override
207    public int getItemViewType(int position) {
208        PresenterSelector presenterSelector = mPresenterSelector != null ?
209                mPresenterSelector : mAdapter.getPresenterSelector();
210        Object item = mAdapter.get(position);
211        Presenter presenter = presenterSelector.getPresenter(item);
212        int type = mPresenters.indexOf(presenter);
213        if (type < 0) {
214            mPresenters.add(presenter);
215            type = mPresenters.indexOf(presenter);
216            if (mAdapterListener != null) {
217                mAdapterListener.onAddPresenter(presenter);
218            }
219        }
220        return type;
221    }
222
223    /**
224     * {@link View.OnFocusChangeListener} that assigned in
225     * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
226     * {@link View.OnFocusChangeListener} after that.
227     */
228    @Override
229    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
230        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
231        Presenter presenter = mPresenters.get(viewType);
232        Presenter.ViewHolder presenterVh;
233        View view;
234        if (mWrapper != null) {
235            view = mWrapper.createWrapper(parent);
236            presenterVh = presenter.onCreateViewHolder(parent);
237            mWrapper.wrap(view, presenterVh.view);
238        } else {
239            presenterVh = presenter.onCreateViewHolder(parent);
240            view = presenterVh.view;
241        }
242        ViewHolder viewHolder = new ViewHolder(presenter, view, presenterVh);
243        if (mAdapterListener != null) {
244            mAdapterListener.onCreate(viewHolder);
245        }
246        View presenterView = viewHolder.mHolder.view;
247        if (presenterView != null) {
248            viewHolder.mFocusChangeListener.mChainedListener = presenterView.getOnFocusChangeListener();
249            presenterView.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
250        }
251        return viewHolder;
252    }
253
254    public void setAdapterListener(AdapterListener listener) {
255        mAdapterListener = listener;
256    }
257
258    @Override
259    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
260        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
261        ViewHolder viewHolder = (ViewHolder) holder;
262        viewHolder.mItem = mAdapter.get(position);
263
264        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
265
266        if (mAdapterListener != null) {
267            mAdapterListener.onBind(viewHolder);
268        }
269    }
270
271    @Override
272    public void onViewRecycled(RecyclerView.ViewHolder holder) {
273        ViewHolder viewHolder = (ViewHolder) holder;
274        viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
275
276        viewHolder.mItem = null;
277
278        if (mAdapterListener != null) {
279            mAdapterListener.onUnbind(viewHolder);
280        }
281    }
282
283    @Override
284    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
285        ViewHolder viewHolder = (ViewHolder) holder;
286        if (mAdapterListener != null) {
287            mAdapterListener.onAttachedToWindow(viewHolder);
288        }
289        viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
290    }
291
292    @Override
293    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
294        ViewHolder viewHolder = (ViewHolder) holder;
295        viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
296        if (mAdapterListener != null) {
297            mAdapterListener.onDetachedFromWindow(viewHolder);
298        }
299    }
300
301    @Override
302    public long getItemId(int position) {
303        return mAdapter.getId(position);
304    }
305
306}
307