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