| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package android.support.v17.leanback.app; |
| |
| import android.content.Context; |
| import android.graphics.Color; |
| import android.graphics.drawable.ColorDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.GradientDrawable; |
| import android.os.Bundle; |
| import android.support.v17.leanback.R; |
| import android.support.v17.leanback.widget.ClassPresenterSelector; |
| import android.support.v17.leanback.widget.DividerPresenter; |
| import android.support.v17.leanback.widget.DividerRow; |
| import android.support.v17.leanback.widget.FocusHighlightHelper; |
| import android.support.v17.leanback.widget.HorizontalGridView; |
| import android.support.v17.leanback.widget.ItemBridgeAdapter; |
| import android.support.v17.leanback.widget.PresenterSelector; |
| import android.support.v17.leanback.widget.Row; |
| import android.support.v17.leanback.widget.RowHeaderPresenter; |
| import android.support.v17.leanback.widget.SectionRow; |
| import android.support.v17.leanback.widget.VerticalGridView; |
| import android.support.v7.widget.RecyclerView; |
| import android.view.View; |
| import android.view.View.OnLayoutChangeListener; |
| import android.view.ViewGroup; |
| import android.widget.FrameLayout; |
| |
| /** |
| * An fragment containing a list of row headers. Implementation must support three types of rows: |
| * <ul> |
| * <li>{@link DividerRow} rendered by {@link DividerPresenter}.</li> |
| * <li>{@link Row} rendered by {@link RowHeaderPresenter}.</li> |
| * <li>{@link SectionRow} rendered by {@link RowHeaderPresenter}.</li> |
| * </ul> |
| * Use {@link #setPresenterSelector(PresenterSelector)} in subclass constructor to customize |
| * Presenters. App may override {@link BrowseFragment#onCreateHeadersFragment()}. |
| */ |
| public class HeadersFragment extends BaseRowFragment { |
| |
| /** |
| * Interface definition for a callback to be invoked when a header item is clicked. |
| */ |
| public interface OnHeaderClickedListener { |
| /** |
| * Called when a header item has been clicked. |
| * |
| * @param viewHolder Row ViewHolder object corresponding to the selected Header. |
| * @param row Row object corresponding to the selected Header. |
| */ |
| void onHeaderClicked(RowHeaderPresenter.ViewHolder viewHolder, Row row); |
| } |
| |
| /** |
| * Interface definition for a callback to be invoked when a header item is selected. |
| */ |
| public interface OnHeaderViewSelectedListener { |
| /** |
| * Called when a header item has been selected. |
| * |
| * @param viewHolder Row ViewHolder object corresponding to the selected Header. |
| * @param row Row object corresponding to the selected Header. |
| */ |
| void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row); |
| } |
| |
| private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener; |
| OnHeaderClickedListener mOnHeaderClickedListener; |
| private boolean mHeadersEnabled = true; |
| private boolean mHeadersGone = false; |
| private int mBackgroundColor; |
| private boolean mBackgroundColorSet; |
| |
| private static final PresenterSelector sHeaderPresenter = new ClassPresenterSelector() |
| .addClassPresenter(DividerRow.class, new DividerPresenter()) |
| .addClassPresenter(SectionRow.class, |
| new RowHeaderPresenter(R.layout.lb_section_header, false)) |
| .addClassPresenter(Row.class, new RowHeaderPresenter(R.layout.lb_header)); |
| |
| public HeadersFragment() { |
| setPresenterSelector(sHeaderPresenter); |
| FocusHighlightHelper.setupHeaderItemFocusHighlight(getBridgeAdapter()); |
| } |
| |
| public void setOnHeaderClickedListener(OnHeaderClickedListener listener) { |
| mOnHeaderClickedListener = listener; |
| } |
| |
| public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) { |
| mOnHeaderViewSelectedListener = listener; |
| } |
| |
| @Override |
| VerticalGridView findGridViewFromRoot(View view) { |
| return (VerticalGridView) view.findViewById(R.id.browse_headers); |
| } |
| |
| @Override |
| void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder, |
| int position, int subposition) { |
| if (mOnHeaderViewSelectedListener != null) { |
| if (viewHolder != null && position >= 0) { |
| ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder; |
| mOnHeaderViewSelectedListener.onHeaderSelected( |
| (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), (Row) vh.getItem()); |
| } else { |
| mOnHeaderViewSelectedListener.onHeaderSelected(null, null); |
| } |
| } |
| } |
| |
| private final ItemBridgeAdapter.AdapterListener mAdapterListener = |
| new ItemBridgeAdapter.AdapterListener() { |
| @Override |
| public void onCreate(final ItemBridgeAdapter.ViewHolder viewHolder) { |
| View headerView = viewHolder.getViewHolder().view; |
| headerView.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| if (mOnHeaderClickedListener != null) { |
| mOnHeaderClickedListener.onHeaderClicked( |
| (RowHeaderPresenter.ViewHolder) viewHolder.getViewHolder(), |
| (Row) viewHolder.getItem()); |
| } |
| } |
| }); |
| if (mWrapper != null) { |
| viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener); |
| } else { |
| headerView.addOnLayoutChangeListener(sLayoutChangeListener); |
| } |
| } |
| |
| }; |
| |
| static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() { |
| @Override |
| public void onLayoutChange(View v, int left, int top, int right, int bottom, |
| int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0); |
| v.setPivotY(v.getMeasuredHeight() / 2); |
| } |
| }; |
| |
| @Override |
| int getLayoutResourceId() { |
| return R.layout.lb_headers_fragment; |
| } |
| |
| @Override |
| public void onViewCreated(View view, Bundle savedInstanceState) { |
| super.onViewCreated(view, savedInstanceState); |
| final VerticalGridView listView = getVerticalGridView(); |
| if (listView == null) { |
| return; |
| } |
| if (mBackgroundColorSet) { |
| listView.setBackgroundColor(mBackgroundColor); |
| updateFadingEdgeToBrandColor(mBackgroundColor); |
| } else { |
| Drawable d = listView.getBackground(); |
| if (d instanceof ColorDrawable) { |
| updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor()); |
| } |
| } |
| updateListViewVisibility(); |
| } |
| |
| private void updateListViewVisibility() { |
| final VerticalGridView listView = getVerticalGridView(); |
| if (listView != null) { |
| getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE); |
| if (!mHeadersGone) { |
| if (mHeadersEnabled) { |
| listView.setChildrenVisibility(View.VISIBLE); |
| } else { |
| listView.setChildrenVisibility(View.INVISIBLE); |
| } |
| } |
| } |
| } |
| |
| void setHeadersEnabled(boolean enabled) { |
| mHeadersEnabled = enabled; |
| updateListViewVisibility(); |
| } |
| |
| void setHeadersGone(boolean gone) { |
| mHeadersGone = gone; |
| updateListViewVisibility(); |
| } |
| |
| static class NoOverlappingFrameLayout extends FrameLayout { |
| |
| public NoOverlappingFrameLayout(Context context) { |
| super(context); |
| } |
| |
| /** |
| * Avoid creating hardware layer for header dock. |
| */ |
| @Override |
| public boolean hasOverlappingRendering() { |
| return false; |
| } |
| } |
| |
| // Wrapper needed because of conflict between RecyclerView's use of alpha |
| // for ADD animations, and RowHeaderPresenter's use of alpha for selected level. |
| final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { |
| @Override |
| public void wrap(View wrapper, View wrapped) { |
| ((FrameLayout) wrapper).addView(wrapped); |
| } |
| |
| @Override |
| public View createWrapper(View root) { |
| return new NoOverlappingFrameLayout(root.getContext()); |
| } |
| }; |
| @Override |
| void updateAdapter() { |
| super.updateAdapter(); |
| ItemBridgeAdapter adapter = getBridgeAdapter(); |
| adapter.setAdapterListener(mAdapterListener); |
| adapter.setWrapper(mWrapper); |
| } |
| |
| void setBackgroundColor(int color) { |
| mBackgroundColor = color; |
| mBackgroundColorSet = true; |
| |
| if (getVerticalGridView() != null) { |
| getVerticalGridView().setBackgroundColor(mBackgroundColor); |
| updateFadingEdgeToBrandColor(mBackgroundColor); |
| } |
| } |
| |
| private void updateFadingEdgeToBrandColor(int backgroundColor) { |
| View fadingView = getView().findViewById(R.id.fade_out_edge); |
| Drawable background = fadingView.getBackground(); |
| if (background instanceof GradientDrawable) { |
| background.mutate(); |
| ((GradientDrawable) background).setColors( |
| new int[] {Color.TRANSPARENT, backgroundColor}); |
| } |
| } |
| |
| @Override |
| public void onTransitionStart() { |
| super.onTransitionStart(); |
| if (!mHeadersEnabled) { |
| // When enabling headers fragment, the RowHeaderView gets a focus but |
| // isShown() is still false because its parent is INVISIBLE, accessibility |
| // event is not sent. |
| // Workaround is: prevent focus to a child view during transition and put |
| // focus on it after transition is done. |
| final VerticalGridView listView = getVerticalGridView(); |
| if (listView != null) { |
| listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); |
| if (listView.hasFocus()) { |
| listView.requestFocus(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onTransitionEnd() { |
| if (mHeadersEnabled) { |
| final VerticalGridView listView = getVerticalGridView(); |
| if (listView != null) { |
| listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); |
| if (listView.hasFocus()) { |
| listView.requestFocus(); |
| } |
| } |
| } |
| super.onTransitionEnd(); |
| } |
| |
| public boolean isScrolling() { |
| return getVerticalGridView().getScrollState() |
| != HorizontalGridView.SCROLL_STATE_IDLE; |
| } |
| } |