ItemBridgeAdapter.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.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    private ObjectAdapter mAdapter;
51    private PresenterSelector mPresenterSelector;
52    private FocusHighlight mFocusHighlight;
53    private AdapterListener mAdapterListener;
54    private ArrayList<Presenter> mPresenters = new ArrayList<Presenter>();
55
56    final class OnFocusChangeListener implements View.OnFocusChangeListener {
57        View.OnFocusChangeListener mChainedListener;
58
59        @Override
60        public void onFocusChange(View view, boolean hasFocus) {
61            if (DEBUG) Log.v(TAG, "onFocusChange " + hasFocus + " " + view
62                    + " mFocusHighlight" + mFocusHighlight);
63            ViewHolder viewHolder = getChildViewHolder(view);
64            if (mFocusHighlight != null) {
65                mFocusHighlight.onItemFocused(view, viewHolder.mItem, hasFocus);
66            }
67            if (mChainedListener != null) {
68                mChainedListener.onFocusChange(view, hasFocus);
69            }
70        }
71    }
72
73    public class ViewHolder extends RecyclerView.ViewHolder {
74        final Presenter mPresenter;
75        final Presenter.ViewHolder mHolder;
76        final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener();
77        Object mItem;
78        Object mExtraObject;
79
80        /**
81         * Get {@link Presenter}.
82         */
83        public final Presenter getPresenter() {
84            return mPresenter;
85        }
86
87        /**
88         * Get {@link Presenter.ViewHolder}.
89         */
90        public final Presenter.ViewHolder getViewHolder() {
91            return mHolder;
92        }
93
94        /**
95         * Get currently bound object.
96         */
97        public final Object getItem() {
98            return mItem;
99        }
100
101        /**
102         * Get extra object associated with the view.  Developer can attach
103         * any customized UI object in addition to {@link Presenter.ViewHolder}.
104         * A typical use case is attaching an animator object.
105         */
106        public final Object getExtraObject() {
107            return mExtraObject;
108        }
109
110        /**
111         * Set extra object associated with the view.  Developer can attach
112         * any customized UI object in addition to {@link Presenter.ViewHolder}.
113         * A typical use case is attaching an animator object.
114         */
115        public void setExtraObject(Object object) {
116            mExtraObject = object;
117        }
118
119        ViewHolder(Presenter presenter, Presenter.ViewHolder holder) {
120            super(holder.view);
121            mPresenter = presenter;
122            mHolder = holder;
123        }
124    }
125
126    private ObjectAdapter.DataObserver mDataObserver = new ObjectAdapter.DataObserver() {
127        @Override
128        public void onChanged() {
129            ItemBridgeAdapter.this.notifyDataSetChanged();
130        }
131        @Override
132        public void onItemRangeChanged(int positionStart, int itemCount) {
133            ItemBridgeAdapter.this.notifyItemRangeChanged(positionStart, itemCount);
134        }
135        @Override
136        public void onItemRangeInserted(int positionStart, int itemCount) {
137            ItemBridgeAdapter.this.notifyItemRangeInserted(positionStart, itemCount);
138        }
139        @Override
140        public void onItemRangeRemoved(int positionStart, int itemCount) {
141            ItemBridgeAdapter.this.notifyItemRangeRemoved(positionStart, itemCount);
142        }
143    };
144
145    public ItemBridgeAdapter(ObjectAdapter adapter, PresenterSelector presenterSelector) {
146        setAdapter(adapter);
147        mPresenterSelector = presenterSelector;
148    }
149
150    public ItemBridgeAdapter() {
151    }
152
153    private ViewHolder getChildViewHolder(View view) {
154        RecyclerView recyclerView = (RecyclerView) view.getParent();
155        return (ViewHolder) recyclerView.getChildViewHolder(view);
156    }
157
158    public void setAdapter(ObjectAdapter adapter) {
159        if (mAdapter != null) {
160            mAdapter.unregisterObserver(mDataObserver);
161        }
162        mAdapter = adapter;
163        if (mAdapter == null) {
164            return;
165        }
166
167        mAdapter.registerObserver(mDataObserver);
168        setHasStableIds(mAdapter.hasStableIds());
169    }
170
171    void setFocusHighlight(FocusHighlight listener) {
172        mFocusHighlight = listener;
173        if (DEBUG) Log.v(TAG, "setFocusHighlight " + mFocusHighlight);
174    }
175
176    public void clear() {
177        setAdapter(null);
178    }
179
180    @Override
181    public int getItemCount() {
182        return mAdapter.size();
183    }
184
185    @Override
186    public int getItemViewType(int position) {
187        PresenterSelector presenterSelector = mPresenterSelector != null ?
188                mPresenterSelector : mAdapter.getPresenterSelector();
189        Object item = mAdapter.get(position);
190        Presenter presenter = presenterSelector.getPresenter(item);
191        int type = mPresenters.indexOf(presenter);
192        if (type < 0) {
193            mPresenters.add(presenter);
194            type = mPresenters.indexOf(presenter);
195            if (mAdapterListener != null) {
196                mAdapterListener.onAddPresenter(presenter);
197            }
198        }
199        return type;
200    }
201
202    /**
203     * {@link View.OnFocusChangeListener} that assigned in
204     * {@link Presenter#onCreateViewHolder(ViewGroup)} may be chained, user should never change
205     * {@link View.OnFocusChangeListener} after that.
206     */
207    @Override
208    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
209        if (DEBUG) Log.v(TAG, "onCreateViewHolder viewType " + viewType);
210        Presenter presenter = mPresenters.get(viewType);
211        ViewHolder viewHolder = new ViewHolder(presenter, presenter.onCreateViewHolder(parent));
212        if (mAdapterListener != null) {
213            mAdapterListener.onCreate(viewHolder);
214        }
215        View view = viewHolder.mHolder.view;
216        if (view != null) {
217            viewHolder.mFocusChangeListener.mChainedListener = view.getOnFocusChangeListener();
218            view.setOnFocusChangeListener(viewHolder.mFocusChangeListener);
219        }
220        return viewHolder;
221    }
222
223    public void setAdapterListener(AdapterListener listener) {
224        mAdapterListener = listener;
225    }
226
227    @Override
228    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
229        if (DEBUG) Log.v(TAG, "onBindViewHolder position " + position);
230        ViewHolder viewHolder = (ViewHolder) holder;
231        viewHolder.mItem = mAdapter.get(position);
232
233        viewHolder.mPresenter.onBindViewHolder(viewHolder.mHolder, viewHolder.mItem);
234
235        if (mAdapterListener != null) {
236            mAdapterListener.onBind(viewHolder);
237        }
238    }
239
240    @Override
241    public void onViewRecycled(RecyclerView.ViewHolder holder) {
242        ViewHolder viewHolder = (ViewHolder) holder;
243        viewHolder.mPresenter.onUnbindViewHolder(viewHolder.mHolder);
244
245        viewHolder.mItem = null;
246
247        if (mAdapterListener != null) {
248            mAdapterListener.onUnbind(viewHolder);
249        }
250    }
251
252    @Override
253    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
254        ViewHolder viewHolder = (ViewHolder) holder;
255        if (mAdapterListener != null) {
256            mAdapterListener.onAttachedToWindow(viewHolder);
257        }
258        viewHolder.mPresenter.onViewAttachedToWindow(viewHolder.mHolder);
259    }
260
261    @Override
262    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
263        ViewHolder viewHolder = (ViewHolder) holder;
264        viewHolder.mPresenter.onViewDetachedFromWindow(viewHolder.mHolder);
265        if (mAdapterListener != null) {
266            mAdapterListener.onDetachedFromWindow(viewHolder);
267        }
268    }
269
270    @Override
271    public long getItemId(int position) {
272        return mAdapter.getId(position);
273    }
274
275}
276