RxTickerColumnManager.java 5.49 KB
Newer Older
konghaorui committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
package com.yidianling.common.view.ticker;

import android.graphics.Canvas;
import android.graphics.Paint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * In ticker, each character in the rendered text is represented by a {@link RxTickerColumn}. The
 * column can be seen as a column of text in which we can animate from one character to the next
 * by scrolling the column vertically. The {@link RxTickerColumnManager} is then a
 * manager/convenience class for handling a list of {@link RxTickerColumn} which then combines into
 * the entire string we are rendering.
 *
 */
class RxTickerColumnManager {
    final ArrayList<RxTickerColumn> mRxTickerColumns = new ArrayList<>();
    private final RxTickerDrawMetrics metrics;

    // The character list that dictates how to transition from one character to another.
    private char[] characterList;
    // A minor optimization so that we can cache the indices of each character.
    private Map<Character, Integer> characterIndicesMap;

    RxTickerColumnManager(RxTickerDrawMetrics metrics) {
        this.metrics = metrics;
    }

    /**
     * @see {@link RxTickerView#setCharacterList(char[])}.
     */
    void setCharacterList(char[] characterList) {
        this.characterList = characterList;
        this.characterIndicesMap = new HashMap<>(characterList.length);

        for (int i = 0; i < characterList.length; i++) {
            characterIndicesMap.put(characterList[i], i);
        }
    }

    /**
     * @return whether or not {@param text} should be debounced because it's the same as the
     *         current target text of this column manager.
     */
    boolean shouldDebounceText(char[] text) {
        final int newTextSize = text.length;
        if (newTextSize == mRxTickerColumns.size()) {
            for (int i = 0; i < newTextSize; i++) {
                if (text[i] != mRxTickerColumns.get(i).getTargetChar()) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Tell the column manager the new target text that it should display.
     */
    void setText(char[] text) {
        if (characterList == null) {
            throw new IllegalStateException("Need to call setCharacterList(char[]) first.");
        }

        // First remove any zero-width columns
        for (int i = 0; i < mRxTickerColumns.size(); ) {
            final RxTickerColumn rxTickerColumn = mRxTickerColumns.get(i);
            if (rxTickerColumn.getCurrentWidth() > 0) {
                i++;
            } else {
                mRxTickerColumns.remove(i);
            }
        }

        // Use Levenshtein distance algorithm to figure out how to manipulate the columns
        final int[] actions = RxLevenshteinUtils.computeColumnActions(getCurrentText(), text);
        int columnIndex = 0;
        int textIndex = 0;
        for (int i = 0; i < actions.length; i++) {
            switch (actions[i]) {
                case RxLevenshteinUtils.ACTION_INSERT:
                    mRxTickerColumns.add(columnIndex,
                            new RxTickerColumn(characterList, characterIndicesMap, metrics));
                case RxLevenshteinUtils.ACTION_SAME:
                    mRxTickerColumns.get(columnIndex).setTargetChar(text[textIndex]);
                    columnIndex++;
                    textIndex++;
                    break;
                case RxLevenshteinUtils.ACTION_DELETE:
                    mRxTickerColumns.get(columnIndex).setTargetChar(RxTickerUtils.EMPTY_CHAR);
                    columnIndex++;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown action: " + actions[i]);
            }
        }
    }

    void onAnimationEnd() {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.onAnimationEnd();
        }
    }

    void setAnimationProgress(float animationProgress) {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.setAnimationProgress(animationProgress);
        }
    }

    float getMinimumRequiredWidth() {
        float width = 0f;
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            width += mRxTickerColumns.get(i).getMinimumRequiredWidth();
        }
        return width;
    }

    float getCurrentWidth() {
        float width = 0f;
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            width += mRxTickerColumns.get(i).getCurrentWidth();
        }
        return width;
    }

    char[] getCurrentText() {
        final int size = mRxTickerColumns.size();
        final char[] currentText = new char[size];
        for (int i = 0; i < size; i++) {
            currentText[i] = mRxTickerColumns.get(i).getCurrentChar();
        }
        return currentText;
    }

    /**
     * This method will draw onto the canvas the appropriate UI state of each column dictated
     * by {@param animationProgress}. As a side effect, this method will also translate the canvas
     * accordingly for the draw procedures.
     */
    void draw(Canvas canvas, Paint textPaint) {
        for (int i = 0, size = mRxTickerColumns.size(); i < size; i++) {
            final RxTickerColumn column = mRxTickerColumns.get(i);
            column.draw(canvas, textPaint);
            canvas.translate(column.getCurrentWidth(), 0f);
        }
    }
}