RowHeaderPresenter.java

/*
 * 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 androidx.leanback.widget;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.graphics.Paint;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.RestrictTo;
import androidx.leanback.R;

/**
 * RowHeaderPresenter provides a default presentation for {@link HeaderItem} using a
 * {@link RowHeaderView} and optionally a TextView for description. If a subclass creates its own
 * view, the subclass must also override {@link #onCreateViewHolder(ViewGroup)},
 * {@link #onSelectLevelChanged(ViewHolder)}.
 */
public class RowHeaderPresenter extends Presenter {

    private final int mLayoutResourceId;
    private final Paint mFontMeasurePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private boolean mNullItemVisibilityGone;
    private final boolean mAnimateSelect;

    /**
     * Creates default RowHeaderPresenter using a title view and a description view.
     * @see ViewHolder#ViewHolder(View)
     */
    public RowHeaderPresenter() {
        this(R.layout.lb_row_header);
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public RowHeaderPresenter(int layoutResourceId) {
        this(layoutResourceId, true);
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP_PREFIX)
    public RowHeaderPresenter(int layoutResourceId, boolean animateSelect) {
        mLayoutResourceId = layoutResourceId;
        mAnimateSelect = animateSelect;
    }

    /**
     * Optionally sets the view visibility to {@link View#GONE} when bound to null.
     */
    public void setNullItemVisibilityGone(boolean nullItemVisibilityGone) {
        mNullItemVisibilityGone = nullItemVisibilityGone;
    }

    /**
     * Returns true if the view visibility is set to {@link View#GONE} when bound to null.
     */
    public boolean isNullItemVisibilityGone() {
        return mNullItemVisibilityGone;
    }

    /**
     * A ViewHolder for the RowHeaderPresenter.
     */
    public static class ViewHolder extends Presenter.ViewHolder {
        float mSelectLevel;
        int mOriginalTextColor;
        float mUnselectAlpha;
        RowHeaderView mTitleView;
        TextView mDescriptionView;

        /**
         * Creating a new ViewHolder that supports title and description.
         * @param view Root of Views.
         */
        public ViewHolder(View view) {
            super(view);
            mTitleView = (RowHeaderView)view.findViewById(R.id.row_header);
            mDescriptionView = (TextView)view.findViewById(R.id.row_header_description);
            initColors();
        }

        /**
         * Uses a single {@link RowHeaderView} for creating a new ViewHolder.
         * @param view The single RowHeaderView.
         * @hide
         */
        @RestrictTo(LIBRARY_GROUP_PREFIX)
        public ViewHolder(RowHeaderView view) {
            super(view);
            mTitleView = view;
            initColors();
        }

        void initColors() {
            if (mTitleView != null) {
                mOriginalTextColor = mTitleView.getCurrentTextColor();
            }

            mUnselectAlpha = view.getResources().getFraction(
                    R.fraction.lb_browse_header_unselect_alpha, 1, 1);
        }

        public final float getSelectLevel() {
            return mSelectLevel;
        }
    }

    @Override
    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
        View root = LayoutInflater.from(parent.getContext())
                .inflate(mLayoutResourceId, parent, false);

        ViewHolder viewHolder = new ViewHolder(root);
        if (mAnimateSelect) {
            setSelectLevel(viewHolder, 0);
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
        HeaderItem headerItem = item == null ? null : ((Row) item).getHeaderItem();
        RowHeaderPresenter.ViewHolder vh = (RowHeaderPresenter.ViewHolder)viewHolder;
        if (headerItem == null) {
            if (vh.mTitleView != null) {
                vh.mTitleView.setText(null);
            }
            if (vh.mDescriptionView != null) {
                vh.mDescriptionView.setText(null);
            }

            viewHolder.view.setContentDescription(null);
            if (mNullItemVisibilityGone) {
                viewHolder.view.setVisibility(View.GONE);
            }
        } else {
            if (vh.mTitleView != null) {
                vh.mTitleView.setText(headerItem.getName());
            }
            if (vh.mDescriptionView != null) {
                if (TextUtils.isEmpty(headerItem.getDescription())) {
                    vh.mDescriptionView.setVisibility(View.GONE);
                } else {
                    vh.mDescriptionView.setVisibility(View.VISIBLE);
                }
                vh.mDescriptionView.setText(headerItem.getDescription());
            }
            viewHolder.view.setContentDescription(headerItem.getContentDescription());
            viewHolder.view.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
        RowHeaderPresenter.ViewHolder vh = (ViewHolder)viewHolder;
        if (vh.mTitleView != null) {
            vh.mTitleView.setText(null);
        }
        if (vh.mDescriptionView != null) {
            vh.mDescriptionView.setText(null);
        }

        if (mAnimateSelect) {
            setSelectLevel((ViewHolder) viewHolder, 0);
        }
    }

    /**
     * Sets the select level.
     */
    public final void setSelectLevel(ViewHolder holder, float selectLevel) {
        holder.mSelectLevel = selectLevel;
        onSelectLevelChanged(holder);
    }

    /**
     * Called when the select level changes.  The default implementation sets the alpha on the view.
     */
    protected void onSelectLevelChanged(ViewHolder holder) {
        if (mAnimateSelect) {
            holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel
                    * (1f - holder.mUnselectAlpha));
        }
    }

    /**
     * Returns the space (distance in pixels) below the baseline of the
     * text view, if one exists; otherwise, returns 0.
     */
    public int getSpaceUnderBaseline(ViewHolder holder) {
        int space = holder.view.getPaddingBottom();
        if (holder.view instanceof TextView) {
            space += (int) getFontDescent((TextView) holder.view, mFontMeasurePaint);
        }
        return space;
    }

    @SuppressWarnings("ReferenceEquality")
    protected static float getFontDescent(TextView textView, Paint fontMeasurePaint) {
        if (fontMeasurePaint.getTextSize() != textView.getTextSize()) {
            fontMeasurePaint.setTextSize(textView.getTextSize());
        }
        if (fontMeasurePaint.getTypeface() != textView.getTypeface()) {
            fontMeasurePaint.setTypeface(textView.getTypeface());
        }
        return fontMeasurePaint.descent();
    }
}