com.amazon.android.ui.widget.ReadTextView.java Source code

Java tutorial

Introduction

Here is the source code for com.amazon.android.ui.widget.ReadTextView.java

Source

/**
 * Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file 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 com.amazon.android.ui.widget;

import com.amazon.utils.R;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.support.v4.content.ContextCompat;
import android.text.Layout;
import android.util.AttributeSet;
import android.widget.TextView;

import uk.co.chrisjenx.calligraphy.CalligraphyConfig;
import uk.co.chrisjenx.calligraphy.CalligraphyUtils;

/**
 * The default text view for the read dialog.
 * <p>
 * Fades the text at top and bottom to hint when scrolling is available and makes sure it fades
 * text, not just empty lines.
 */
public class ReadTextView extends TextView {

    /**
     * Defines the percentage of a page that the system will scroll when receive a Up/Down event
     * <p>
     * Set to 18% this was determined with inspection with the current size and font size to be
     * two lines of text.  A better solution would be to get the current height and inspect
     * the text metrics and calculate dynamically.
     */
    private static final float MAX_SCROLL_AMOUNT = 0.18f;

    private TextView mText;
    private ScrollViewPlus mScrollView;

    /**
     * {@inheritDoc}
     */
    public ReadTextView(final Context context) {

        super(context);
    }

    /**
     * {@inheritDoc}
     */
    public ReadTextView(final Context context, final AttributeSet attrs, final int defStyle) {

        super(context, attrs, defStyle);
    }

    /**
     * {@inheritDoc}
     */
    public ReadTextView(final Context context, final AttributeSet attrs) {

        super(context, attrs);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onFinishInflate() {

        super.onFinishInflate();

        mText = (TextView) findViewById(R.id.txt);

        // Font needs to be applied since this was created dynamically.
        CalligraphyUtils.applyFontToTextView(getContext(), mText, CalligraphyConfig.get(), null);
        mText.setTextColor(ContextCompat.getColor(getContext(), R.color.primary_text));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onAttachedToWindow() {

        super.onAttachedToWindow();

        if (mText != null && mText.getParent() != null && mText.getParent() instanceof ScrollViewPlus) {

            // For some reason setScrollbarFadingEnabled is not working,
            // hence adding very long duration before the scroll bar starts fading.
            mScrollView = (ScrollViewPlus) mText.getParent();
            mScrollView.setMaxScrollAmount(MAX_SCROLL_AMOUNT);
            mScrollView.setScrollBarFadeDuration(500000);
            mScrollView.setScrollBarStyle(SCROLLBARS_OUTSIDE_INSET);
            mScrollView.setPadding(14, 0, 14, 0);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onDraw(final Canvas canvas) {
        // Update the gradient each time the text is drawn.
        updateGradient();

        super.onDraw(canvas);
    }

    /**
     * Draw a gradient on the text so that the bottom and top line have a faded effect if the user
     * can scroll in that direction.
     */
    private void updateGradient() {

        if (mText == null || mScrollView == null) {
            return;
        }

        final Layout layout = mText.getLayout();
        assert layout != null;

        final Rect bounds = new Rect();
        getTextBounds(bounds);
        final int topLine = topVisibleLineOfText(layout, bounds);
        final int bottomLine = bottomVisibleLineOfText(layout, bounds);
        final int fullHeight = layout.getHeight();

        if (fullHeight > 0) {

            final float bottomLineHeight = layout.getLineBottom(bottomLine) - layout.getLineTop(bottomLine);
            final float topLineHeight = layout.getLineBottom(topLine) - layout.getLineTop(topLine);
            this.setFadingEdgeLength((int) Math.max(topLineHeight, bottomLineHeight));
        }
    }

    /**
     * Which line in the layout is the top visible line.
     *
     * @param layout Calculated text layout.
     * @param bounds Visible bounds in the coordinates of the TextView and layout.
     * @return The top visible line.
     */
    private int topVisibleLineOfText(final Layout layout, final Rect bounds) {

        final int lineCount = layout.getLineCount();
        for (int lineNum = 0; lineNum < lineCount; lineNum++) {
            final int top = layout.getLineTop(lineNum);
            if (top >= bounds.top && top <= bounds.bottom) {
                return lineNum;
            }
        }
        return lineCount;
    }

    /**
     * Which line in the layout is the bottom visible line.
     *
     * @param layout Calculated text layout.
     * @param bounds Visible bounds in the coordinates of the TextView and layout.
     * @return The bottom visible line
     */
    private int bottomVisibleLineOfText(final Layout layout, final Rect bounds) {

        final int lineCount = layout.getLineCount();
        for (int lineNum = lineCount - 1; lineNum >= 0; lineNum--) {
            final int bottom = layout.getLineBottom(lineNum);
            if (bottom >= bounds.top && bottom <= bounds.bottom) {
                return lineNum;
            }
        }
        return 0;
    }

    /**
     * The out Rect will contain the visible area of the scrolled mText in mText coordinates
     *
     * @param outRect The outer rectangle.
     */
    private void getTextBounds(final Rect outRect) {

        mScrollView.getDrawingRect(outRect);
        mScrollView.offsetRectIntoDescendantCoords(mText, outRect);
    }
}