DetailsOverviewRowPresenter.java revision 819b4e55bcf37847548a55c5dac0dfa8323975f6
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.content.Context;
17import android.graphics.Color;
18import android.support.v17.leanback.R;
19import android.support.v7.widget.RecyclerView;
20import android.util.Log;
21import android.util.TypedValue;
22import android.view.LayoutInflater;
23import android.view.View;
24import android.view.ViewGroup;
25import android.widget.FrameLayout;
26import android.widget.ImageView;
27
28import java.util.Collection;
29
30/**
31 * A DetailsOverviewRowPresenter renders a {@link DetailsOverviewRow} to display an
32 * overview of an item. Typically this row will be the first row in a fragment
33 * such as the {@link android.support.v17.leanback.app.DetailsFragment
34 * DetailsFragment}.
35 *
36 * <p>The detailed description is rendered using a {@link Presenter}.
37 */
38public class DetailsOverviewRowPresenter extends RowPresenter {
39
40    private static final String TAG = "DetailsOverviewRowPresenter";
41    private static final boolean DEBUG = false;
42
43    private static final int MORE_ACTIONS_FADE_MS = 100;
44
45    /**
46     * A ViewHolder for the DetailsOverviewRow.
47     */
48    public static class ViewHolder extends RowPresenter.ViewHolder {
49        final ImageView mImageView;
50        final FrameLayout mDetailsDescriptionFrame;
51        final HorizontalGridView mActionsRow;
52        Presenter.ViewHolder mDetailsDescriptionViewHolder;
53        int mNumItems;
54        boolean mShowMoreRight;
55        boolean mShowMoreLeft;
56
57        void bind(ItemBridgeAdapter bridgeAdapter) {
58            mNumItems = bridgeAdapter.getItemCount();
59            bridgeAdapter.setAdapterListener(mAdapterListener);
60
61            mShowMoreRight = false;
62            mShowMoreLeft = true;
63            showMoreLeft(false);
64        }
65
66        final View.OnLayoutChangeListener mLayoutChangeListener =
67                new View.OnLayoutChangeListener() {
68
69            @Override
70            public void onLayoutChange(View v, int left, int top, int right,
71                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
72                if (DEBUG) Log.v(TAG, "onLayoutChange " + v);
73                checkFirstAndLastPosition(false);
74            }
75        };
76
77        final ItemBridgeAdapter.AdapterListener mAdapterListener =
78                new ItemBridgeAdapter.AdapterListener() {
79
80            @Override
81            public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
82                // Remove first to ensure we don't add ourselves more than once.
83                viewHolder.itemView.removeOnLayoutChangeListener(mLayoutChangeListener);
84                viewHolder.itemView.addOnLayoutChangeListener(mLayoutChangeListener);
85            }
86            @Override
87            public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder viewHolder) {
88                viewHolder.itemView.removeOnLayoutChangeListener(mLayoutChangeListener);
89                checkFirstAndLastPosition(false);
90            }
91        };
92
93        final RecyclerView.OnScrollListener mScrollListener =
94                new RecyclerView.OnScrollListener() {
95
96            @Override
97            public void onScrollStateChanged(int newState) {
98            }
99            @Override
100            public void onScrolled(int dx, int dy) {
101                checkFirstAndLastPosition(true);
102            }
103        };
104
105        private int getViewCenter(View view) {
106            return (view.getRight() - view.getLeft()) / 2;
107        }
108
109        private void checkFirstAndLastPosition(boolean fromScroll) {
110            RecyclerView.ViewHolder viewHolder;
111
112            viewHolder = mActionsRow.findViewHolderForPosition(mNumItems - 1);
113            boolean showRight = (viewHolder == null ||
114                    viewHolder.itemView.getRight() > mActionsRow.getWidth());
115
116            viewHolder = mActionsRow.findViewHolderForPosition(0);
117            boolean showLeft = (viewHolder == null || viewHolder.itemView.getLeft() < 0);
118
119            if (DEBUG) Log.v(TAG, "checkFirstAndLast fromScroll " + fromScroll +
120                    " showRight " + showRight + " showLeft " + showLeft);
121
122            showMoreRight(showRight);
123            showMoreLeft(showLeft);
124        }
125
126        private void showMoreLeft(boolean show) {
127            if (show != mShowMoreLeft) {
128                mActionsRow.setFadingLeftEdge(show);
129                mShowMoreLeft = show;
130            }
131        }
132
133        private void showMoreRight(boolean show) {
134            if (show != mShowMoreRight) {
135                mActionsRow.setFadingRightEdge(show);
136                mShowMoreRight = show;
137            }
138        }
139
140        /**
141         * Constructor for the ViewHolder.
142         *
143         * @param rootView The root View that this view holder will be attached
144         *        to.
145         */
146        public ViewHolder(View rootView) {
147            super(rootView);
148            mImageView = (ImageView) rootView.findViewById(R.id.details_overview_image);
149            mDetailsDescriptionFrame =
150                    (FrameLayout) rootView.findViewById(R.id.details_overview_description);
151            mActionsRow =
152                    (HorizontalGridView) rootView.findViewById(R.id.details_overview_actions);
153            mActionsRow.setOnScrollListener(mScrollListener);
154
155            final int fadeLength = rootView.getResources().getDimensionPixelSize(
156                    R.dimen.lb_details_overview_actions_fade_size);
157            mActionsRow.setFadingRightEdgeLength(fadeLength);
158            mActionsRow.setFadingLeftEdgeLength(fadeLength);
159        }
160    }
161
162    private final Presenter mDetailsPresenter;
163    private final ActionPresenterSelector mActionPresenterSelector;
164    private final ItemBridgeAdapter mActionBridgeAdapter;
165    private int mBackgroundColor = Color.TRANSPARENT;
166    private boolean mBackgroundColorSet;
167    private boolean mIsStyleLarge = true;
168
169    /**
170     * Constructor for a DetailsOverviewRowPresenter.
171     *
172     * @param detailsPresenter The {@link Presenter} used to render the detailed
173     *        description of the row.
174     */
175    public DetailsOverviewRowPresenter(Presenter detailsPresenter) {
176        setHeaderPresenter(null);
177        setSelectEffectEnabled(false);
178        mDetailsPresenter = detailsPresenter;
179        mActionPresenterSelector = new ActionPresenterSelector();
180        mActionBridgeAdapter = new ItemBridgeAdapter();
181    }
182
183    /**
184     * Sets the listener for Action click events.
185     */
186    public void setOnActionClickedListener(OnActionClickedListener listener) {
187        mActionPresenterSelector.setOnActionClickedListener(listener);
188    }
189
190    /**
191     * Gets the listener for Action click events.
192     */
193    public OnActionClickedListener getOnActionClickedListener() {
194        return mActionPresenterSelector.getOnActionClickedListener();
195    }
196
197    /**
198     * Sets the background color.  If not set, a default from the theme will be used.
199     */
200    public void setBackgroundColor(int color) {
201        mBackgroundColor = color;
202        mBackgroundColorSet = true;
203    }
204
205    /**
206     * Returns the background color.  If no background color was set, transparent
207     * is returned.
208     */
209    public int getBackgroundColor() {
210        return mBackgroundColor;
211    }
212
213    /**
214     * Sets the layout style to be large or small. This affects the height of
215     * the overview, including the text description. The default is large.
216     */
217    public void setStyleLarge(boolean large) {
218        mIsStyleLarge = large;
219    }
220
221    /**
222     * Returns true if the layout style is large.
223     */
224    public boolean isStyleLarge() {
225        return mIsStyleLarge;
226    }
227
228    private int getDefaultBackgroundColor(Context context) {
229        TypedValue outValue = new TypedValue();
230        context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true);
231        return context.getResources().getColor(outValue.resourceId);
232    }
233
234    @Override
235    protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
236        View v = LayoutInflater.from(parent.getContext())
237            .inflate(R.layout.lb_details_overview, parent, false);
238        ViewHolder vh = new ViewHolder(v);
239
240        vh.mDetailsDescriptionViewHolder =
241            mDetailsPresenter.onCreateViewHolder(vh.mDetailsDescriptionFrame);
242        vh.mDetailsDescriptionFrame.addView(vh.mDetailsDescriptionViewHolder.view);
243
244        initDetailsOverview(vh);
245
246        return vh;
247    }
248
249    private void initDetailsOverview(ViewHolder vh) {
250        int resId = mIsStyleLarge ? R.dimen.lb_details_overview_height_large :
251            R.dimen.lb_details_overview_height_small;
252
253        View overview = vh.view.findViewById(R.id.details_overview);
254        ViewGroup.LayoutParams lp = overview.getLayoutParams();
255        lp.height = overview.getResources().getDimensionPixelSize(resId);
256        overview.setLayoutParams(lp);
257
258        overview.setBackgroundColor(mBackgroundColorSet ?
259                mBackgroundColor : getDefaultBackgroundColor(overview.getContext()));
260        ShadowHelper.getInstance().setZ(overview, 0f);
261
262        // Max width to make a square
263        ImageView image = (ImageView) vh.view.findViewById(R.id.details_overview_image);
264        image.setMaxWidth(lp.height);
265    }
266
267    @Override
268    protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
269        super.onBindRowViewHolder(holder, item);
270
271        DetailsOverviewRow row = (DetailsOverviewRow) item;
272        ViewHolder vh = (ViewHolder) holder;
273        if (row.getImageDrawable() != null) {
274            vh.mImageView.setImageDrawable(row.getImageDrawable());
275        }
276        if (vh.mDetailsDescriptionViewHolder == null) {
277        }
278        mDetailsPresenter.onBindViewHolder(vh.mDetailsDescriptionViewHolder, row);
279
280        mActionBridgeAdapter.clear();
281        ArrayObjectAdapter aoa = new ArrayObjectAdapter(mActionPresenterSelector);
282        aoa.addAll(0, (Collection)row.getActions());
283
284        mActionBridgeAdapter.setAdapter(aoa);
285        vh.mActionsRow.setAdapter(mActionBridgeAdapter);
286
287        vh.bind(mActionBridgeAdapter);
288    }
289
290    @Override
291    protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
292        super.onUnbindRowViewHolder(holder);
293
294        ViewHolder vh = (ViewHolder) holder;
295        if (vh.mDetailsDescriptionViewHolder != null) {
296            mDetailsPresenter.onUnbindViewHolder(vh.mDetailsDescriptionViewHolder);
297        }
298
299        vh.mActionsRow.setAdapter(null);
300    }
301}
302