diff --git a/README.md b/README.md index 69110dc..23e2aff 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ android:gravity="center" android:textColor="@color/colorPrimary" android:textSize="16sp" app:ticker_animationDuration="1500" +app:ticker_preferredScrollingDirection="any" ``` Or Java: @@ -79,6 +80,7 @@ tickerView.setTypeface(myCustomTypeface); tickerView.setAnimationDuration(500); tickerView.setAnimationInterpolator(new OvershootInterpolator()); tickerView.setGravity(Gravity.START); +tickerView.setPreferredScrollingDirection(TickerView.ScrollingDirection.ANY); ``` For the full list of XML attributes that we support, please refer to the diff --git a/ticker-sample/src/main/java/com/robinhood/ticker/sample/MainActivity.java b/ticker-sample/src/main/java/com/robinhood/ticker/sample/MainActivity.java index 67c1379..1815562 100644 --- a/ticker-sample/src/main/java/com/robinhood/ticker/sample/MainActivity.java +++ b/ticker-sample/src/main/java/com/robinhood/ticker/sample/MainActivity.java @@ -22,6 +22,10 @@ protected void onCreate(Bundle savedInstanceState) { ticker2 = findViewById(R.id.ticker2); ticker3 = findViewById(R.id.ticker3); + ticker1.setPreferredScrollingDirection(TickerView.ScrollingDirection.DOWN); + ticker2.setPreferredScrollingDirection(TickerView.ScrollingDirection.UP); + ticker3.setPreferredScrollingDirection(TickerView.ScrollingDirection.ANY); + findViewById(R.id.perfBtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/ticker/src/main/java/com/robinhood/ticker/TickerCharacterList.java b/ticker/src/main/java/com/robinhood/ticker/TickerCharacterList.java index ba31857..7b01f59 100644 --- a/ticker/src/main/java/com/robinhood/ticker/TickerCharacterList.java +++ b/ticker/src/main/java/com/robinhood/ticker/TickerCharacterList.java @@ -64,33 +64,55 @@ class TickerCharacterList { /** * @param start the character that we want to animate from * @param end the character that we want to animate to + * @param direction the preferred {@Link TickerView#ScrollingDirection} * @return a valid pair of start and end indices, or null if the inputs are not supported. */ - CharacterIndices getCharacterIndices(char start, char end) { + CharacterIndices getCharacterIndices(char start, char end, TickerView.ScrollingDirection direction) { int startIndex = getIndexOfChar(start); int endIndex = getIndexOfChar(end); + if (startIndex < 0 || endIndex < 0) { return null; } - // see if the wrap-around animation is shorter distance than the original animation - if (start != TickerUtils.EMPTY_CHAR && end != TickerUtils.EMPTY_CHAR) { - if (endIndex < startIndex) { - // If we are potentially going backwards - final int nonWrapDistance = startIndex - endIndex; - final int wrapDistance = numOriginalCharacters - startIndex + endIndex; - if (wrapDistance < nonWrapDistance) { + switch (direction) { + case DOWN: + if (end == TickerUtils.EMPTY_CHAR) { + endIndex = characterList.length; + } else if (endIndex < startIndex) { endIndex += numOriginalCharacters; } - } else if (startIndex < endIndex) { - // If we are potentially going forwards - final int nonWrapDistance = endIndex - startIndex; - final int wrapDistance = numOriginalCharacters - endIndex + startIndex; - if (wrapDistance < nonWrapDistance) { + + break; + case UP: + if (startIndex < endIndex) { startIndex += numOriginalCharacters; } - } + + break; + case ANY: + // see if the wrap-around animation is shorter distance than the original animation + if (start != TickerUtils.EMPTY_CHAR && end != TickerUtils.EMPTY_CHAR) { + if (endIndex < startIndex) { + // If we are potentially going backwards + final int nonWrapDistance = startIndex - endIndex; + final int wrapDistance = numOriginalCharacters - startIndex + endIndex; + if (wrapDistance < nonWrapDistance) { + endIndex += numOriginalCharacters; + } + } else if (startIndex < endIndex) { + // If we are potentially going forwards + final int nonWrapDistance = endIndex - startIndex; + final int wrapDistance = numOriginalCharacters - endIndex + startIndex; + if (wrapDistance < nonWrapDistance) { + startIndex += numOriginalCharacters; + } + } + } + + break; } + return new CharacterIndices(startIndex, endIndex); } diff --git a/ticker/src/main/java/com/robinhood/ticker/TickerColumn.java b/ticker/src/main/java/com/robinhood/ticker/TickerColumn.java index 6f62e76..555ff7e 100644 --- a/ticker/src/main/java/com/robinhood/ticker/TickerColumn.java +++ b/ticker/src/main/java/com/robinhood/ticker/TickerColumn.java @@ -114,7 +114,7 @@ private void setCharacterIndices() { for (int i = 0; i < characterLists.length; i++) { final TickerCharacterList.CharacterIndices indices = - characterLists[i].getCharacterIndices(currentChar, targetChar); + characterLists[i].getCharacterIndices(currentChar, targetChar, metrics.getPreferredScrollingDirection()); if (indices != null) { this.currentCharacterList = this.characterLists[i].getCharacterList(); this.startIndex = indices.startIndex; diff --git a/ticker/src/main/java/com/robinhood/ticker/TickerDrawMetrics.java b/ticker/src/main/java/com/robinhood/ticker/TickerDrawMetrics.java index a46da4f..0dae7fb 100644 --- a/ticker/src/main/java/com/robinhood/ticker/TickerDrawMetrics.java +++ b/ticker/src/main/java/com/robinhood/ticker/TickerDrawMetrics.java @@ -36,6 +36,8 @@ class TickerDrawMetrics { private final Map charWidths = new HashMap<>(256); private float charHeight, charBaseline; + private TickerView.ScrollingDirection preferredScrollingDirection = TickerView.ScrollingDirection.ANY; + TickerDrawMetrics(Paint textPaint) { this.textPaint = textPaint; invalidate(); @@ -71,4 +73,12 @@ float getCharHeight() { float getCharBaseline() { return charBaseline; } + + TickerView.ScrollingDirection getPreferredScrollingDirection() { + return preferredScrollingDirection; + } + + void setPreferredScrollingDirection(TickerView.ScrollingDirection preferredScrollingDirection) { + this.preferredScrollingDirection = preferredScrollingDirection; + } } diff --git a/ticker/src/main/java/com/robinhood/ticker/TickerView.java b/ticker/src/main/java/com/robinhood/ticker/TickerView.java index dae7007..19a1be6 100644 --- a/ticker/src/main/java/com/robinhood/ticker/TickerView.java +++ b/ticker/src/main/java/com/robinhood/ticker/TickerView.java @@ -57,6 +57,11 @@ * @author Jin Cao, Robinhood */ public class TickerView extends View { + + public enum ScrollingDirection { + ANY, UP, DOWN + } + private static final int DEFAULT_TEXT_SIZE = 12; private static final int DEFAULT_TEXT_COLOR = Color.BLACK; private static final int DEFAULT_ANIMATION_DURATION = 350; @@ -179,6 +184,23 @@ protected void init(Context context, AttributeSet attrs, int defStyleAttr, int d } } + final int defaultPreferredScrollingDirection = + arr.getInt(R.styleable.TickerView_ticker_defaultPreferredScrollingDirection, 0); + + switch (defaultPreferredScrollingDirection) { + case 0: + metrics.setPreferredScrollingDirection(ScrollingDirection.ANY); + break; + case 1: + metrics.setPreferredScrollingDirection(ScrollingDirection.UP); + break; + case 2: + metrics.setPreferredScrollingDirection(ScrollingDirection.DOWN); + break; + default: + throw new IllegalArgumentException("Unsupported ticker_defaultPreferredScrollingDirection: " + defaultPreferredScrollingDirection); + } + if (isCharacterListsSet()) { setText(styledAttributes.text, false); } else { @@ -455,6 +477,19 @@ public void setAnimationInterpolator(Interpolator animationInterpolator) { this.animationInterpolator = animationInterpolator; } + /** + * Sets the preferred scrolling direction for ticker animations. + * Eligible params include {@link ScrollingDirection#ANY}, {@link ScrollingDirection#UP} + * and {@link ScrollingDirection#DOWN}. + * + * The default value is {@link ScrollingDirection#ANY}. + * + * @param direction the preferred {@link ScrollingDirection} + */ + public void setPreferredScrollingDirection(ScrollingDirection direction) { + this.metrics.setPreferredScrollingDirection(direction); + } + /** * @return the current text gravity used to align the text. Should be one of the values defined * in {@link android.view.Gravity}. diff --git a/ticker/src/main/res/values/attrs.xml b/ticker/src/main/res/values/attrs.xml index 223cb3f..b32dd48 100644 --- a/ticker/src/main/res/values/attrs.xml +++ b/ticker/src/main/res/values/attrs.xml @@ -7,6 +7,11 @@ + + + + + diff --git a/ticker/src/test/java/com/robinhood/ticker/TickerCharacterListTest.java b/ticker/src/test/java/com/robinhood/ticker/TickerCharacterListTest.java index 4be837e..07e6758 100644 --- a/ticker/src/test/java/com/robinhood/ticker/TickerCharacterListTest.java +++ b/ticker/src/test/java/com/robinhood/ticker/TickerCharacterListTest.java @@ -17,7 +17,7 @@ public void test_initialization() { @Test public void test_getCharacterIndices() { final TickerCharacterList list = new TickerCharacterList("012"); - final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '1'); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '1', TickerView.ScrollingDirection.ANY); assertEquals(1, indices.startIndex); assertEquals(2, indices.endIndex); } @@ -25,7 +25,7 @@ public void test_getCharacterIndices() { @Test public void test_getCharacterIndicesWraparound() { final TickerCharacterList list = new TickerCharacterList("012"); - final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('2', '0'); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('2', '0', TickerView.ScrollingDirection.ANY); assertEquals(3, indices.startIndex); assertEquals(4, indices.endIndex); } @@ -33,7 +33,39 @@ public void test_getCharacterIndicesWraparound() { @Test public void test_getCharacterIndicesWraparound2() { final TickerCharacterList list = new TickerCharacterList("012"); - final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '2'); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '2', TickerView.ScrollingDirection.ANY); + assertEquals(4, indices.startIndex); + assertEquals(3, indices.endIndex); + } + + @Test + public void test_getCharacterIndicesForcedDown() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('2', '0', TickerView.ScrollingDirection.DOWN); + assertEquals(3, indices.startIndex); + assertEquals(4, indices.endIndex); + } + + @Test + public void test_getCharacterIndicesForcedDown2() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '2', TickerView.ScrollingDirection.DOWN); + assertEquals(1, indices.startIndex); + assertEquals(3, indices.endIndex); + } + + @Test + public void test_getCharacterIndicesForcedUp() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('2', '0', TickerView.ScrollingDirection.UP); + assertEquals(3, indices.startIndex); + assertEquals(1, indices.endIndex); + } + + @Test + public void test_getCharacterIndicesForcedUp2() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = list.getCharacterIndices('0', '2', TickerView.ScrollingDirection.UP); assertEquals(4, indices.startIndex); assertEquals(3, indices.endIndex); } @@ -42,8 +74,26 @@ public void test_getCharacterIndicesWraparound2() { public void test_getCharacterIndicesEmptyNoWraparound() { final TickerCharacterList list = new TickerCharacterList("012"); final TickerCharacterList.CharacterIndices indices = - list.getCharacterIndices('2', TickerUtils.EMPTY_CHAR); + list.getCharacterIndices('2', TickerUtils.EMPTY_CHAR, TickerView.ScrollingDirection.ANY); + assertEquals(3, indices.startIndex); + assertEquals(0, indices.endIndex); + } + + @Test + public void test_getCharacterIndicesEmptyForcedUp() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = + list.getCharacterIndices('2', TickerUtils.EMPTY_CHAR, TickerView.ScrollingDirection.UP); assertEquals(3, indices.startIndex); assertEquals(0, indices.endIndex); } + + @Test + public void test_getCharacterIndicesEmptyForcedDown() { + final TickerCharacterList list = new TickerCharacterList("012"); + final TickerCharacterList.CharacterIndices indices = + list.getCharacterIndices('2', TickerUtils.EMPTY_CHAR, TickerView.ScrollingDirection.DOWN); + assertEquals(3, indices.startIndex); + assertEquals(7, indices.endIndex); + } } diff --git a/ticker/src/test/java/com/robinhood/ticker/TickerColumnManagerTest.java b/ticker/src/test/java/com/robinhood/ticker/TickerColumnManagerTest.java index b096544..e7a0a4f 100644 --- a/ticker/src/test/java/com/robinhood/ticker/TickerColumnManagerTest.java +++ b/ticker/src/test/java/com/robinhood/ticker/TickerColumnManagerTest.java @@ -20,6 +20,7 @@ public void setup() { when(metrics.getCharWidth(anyChar())).thenReturn(5f); when(metrics.getCharWidth(TickerUtils.EMPTY_CHAR)).thenReturn(0f); + when(metrics.getPreferredScrollingDirection()).thenReturn(TickerView.ScrollingDirection.ANY); tickerColumnManager = new TickerColumnManager(metrics); tickerColumnManager.setCharacterLists("1234567890"); diff --git a/ticker/src/test/java/com/robinhood/ticker/TickerColumnTest.java b/ticker/src/test/java/com/robinhood/ticker/TickerColumnTest.java index b4d90d4..06b1fb9 100644 --- a/ticker/src/test/java/com/robinhood/ticker/TickerColumnTest.java +++ b/ticker/src/test/java/com/robinhood/ticker/TickerColumnTest.java @@ -35,6 +35,8 @@ public void setup() { when(metrics.getCharHeight()).thenReturn(CHAR_HEIGHT); when(metrics.getCharWidth(anyChar())).thenReturn(DEFAULT_CHAR_WIDTH); when(metrics.getCharWidth(TickerUtils.EMPTY_CHAR)).thenReturn(0f); + when(metrics.getPreferredScrollingDirection()).thenReturn(TickerView.ScrollingDirection.ANY); + tickerColumn = new TickerColumn( new TickerCharacterList[] { characterList }, metrics