diff --git a/base/src/main/java/com/heyongrui/base/widget/numberruntextview/BigDecimalEvaluator.java b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/BigDecimalEvaluator.java new file mode 100644 index 0000000..b58288b --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/BigDecimalEvaluator.java @@ -0,0 +1,19 @@ +package com.heyongrui.base.widget.numberruntextview; + +import android.animation.TypeEvaluator; + +import java.math.BigDecimal; + +/** + * Created by lambert on 2018/10/11. + */ + +public class BigDecimalEvaluator implements TypeEvaluator { + @Override + public Object evaluate(float fraction, Object startValue, Object endValue) { + BigDecimal start = (BigDecimal) startValue; + BigDecimal end = (BigDecimal) endValue; + BigDecimal result = end.subtract(start); + return result.multiply(new BigDecimal("" + fraction)).add(start); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/heyongrui/base/widget/numberruntextview/EditTextWithClear.java b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/EditTextWithClear.java new file mode 100644 index 0000000..301b944 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/EditTextWithClear.java @@ -0,0 +1,101 @@ +package com.heyongrui.base.widget.numberruntextview; + +import android.text.Editable; +import android.text.Selection; +import android.text.TextWatcher; +import android.widget.EditText; + +/** + * Created by lambert on 2018/10/11. + * 银行卡号输入框格式(每4位有个空格) + */ + +public class EditTextWithClear implements TextWatcher { + + private EditText mEditText; + private char mDivider; + + int beforeTextLength = 0; + int onTextLength = 0; + boolean isChanged = false; + + int location = 0;// 记录光标的位置 + private char[] tempChar; + private StringBuffer buffer = new StringBuffer(); + int konggeNumberB = 0; + + public EditTextWithClear(EditText editText, char divider) { + mEditText = editText; + mDivider = divider; + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, + int count) { + onTextLength = s.length(); + buffer.append(s.toString()); + if (onTextLength == beforeTextLength || onTextLength <= 3 + || isChanged) { + isChanged = false; + return; + } + isChanged = true; + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + beforeTextLength = s.length(); + if (buffer.length() > 0) { + buffer.delete(0, buffer.length()); + } + konggeNumberB = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == mDivider) { + konggeNumberB++; + } + } + } + + @Override + public void afterTextChanged(Editable s) { + if (isChanged) { + location = mEditText.getSelectionEnd(); + int index = 0; + while (index < buffer.length()) { + if (buffer.charAt(index) == mDivider) { + buffer.deleteCharAt(index); + } else { + index++; + } + } + + index = 0; + int spaceNumberCount = 0;//空格的个数 + while (index < buffer.length()) { + if ((index == 4 || index == 9 || index == 14 || index == 19 || index == 24 || index == 29)) { + buffer.insert(index, mDivider); + spaceNumberCount++; + } + index++; + } + + if (spaceNumberCount > konggeNumberB) { + location += (spaceNumberCount - konggeNumberB); + } + + tempChar = new char[buffer.length()]; + buffer.getChars(0, buffer.length(), tempChar, 0); + String str = buffer.toString(); + if (location > str.length()) { + location = str.length(); + } else if (location < 0) { + location = 0; + } + mEditText.setText(str); + Editable etable = mEditText.getText(); + Selection.setSelection(etable, location); + isChanged = false; + } + } +} \ No newline at end of file diff --git a/base/src/main/java/com/heyongrui/base/widget/numberruntextview/NumberRunningTextView.java b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/NumberRunningTextView.java new file mode 100644 index 0000000..2bb0ea1 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/numberruntextview/NumberRunningTextView.java @@ -0,0 +1,198 @@ +package com.heyongrui.base.widget.numberruntextview; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.heyongrui.base.R; + +import java.math.BigDecimal; +import java.text.DecimalFormat; + + +/** + * Created by lambert on 2018/10/11. + */ + +@SuppressLint("AppCompatCustomView") +public class NumberRunningTextView extends TextView { + + private static final int MONEY_TYPE = 0; + private static final int NUM_TYPE = 1; + + private int textType;//内容的类型,默认是金钱类型 + private boolean useCommaFormat;//是否使用每三位数字一个逗号的格式,让数字显得比较好看,默认使用 + private boolean runWhenChange;//是否当内容有改变才使用动画,默认是 + private int duration;//动画的周期,默认为800ms + private int minNum;//显示数字最少要达到这个数字才滚动 默认为1 + private float minMoney;//显示金额最少要达到这个数字才滚动 默认为0.3 + private Animator.AnimatorListener animatorListener; + + private DecimalFormat formatter = new DecimalFormat("0.00");// 格式化金额,保留两位小数 + private String preStr; + + + public NumberRunningTextView(Context context) { + this(context, null); + } + + public NumberRunningTextView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.textViewStyle); + } + + public NumberRunningTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NumberRunningTextView); + duration = ta.getInt(R.styleable.NumberRunningTextView_duration, 1000); + textType = ta.getInt(R.styleable.NumberRunningTextView_textType, MONEY_TYPE); + useCommaFormat = ta.getBoolean(R.styleable.NumberRunningTextView_useCommaFormat, true); + runWhenChange = ta.getBoolean(R.styleable.NumberRunningTextView_runWhenChange, true); + minNum = ta.getInt(R.styleable.NumberRunningTextView_minNum, 3); + minMoney = ta.getFloat(R.styleable.NumberRunningTextView_minMoney, 0.1f); + + ta.recycle(); + } + + + /** + * 设置需要滚动的金钱(必须为正数)或整数(必须为正数)的字符串 + * + * @param str + */ + public void setContent(String str) { + //如果是当内容改变的时候才执行滚动动画,判断内容是否有变化 + if (runWhenChange) { + if (TextUtils.isEmpty(preStr)) { + //如果上一次的str为空 + preStr = str; + useAnimByType(str); + return; + } + + //如果上一次的str不为空,判断两次内容是否一致 + if (preStr.equals(str)) { + //如果两次内容一致,则不做处理 + return; + } + + preStr = str;//如果两次内容不一致,记录最新的str + } + + useAnimByType(str); + } + + private void useAnimByType(String str) { + if (textType == MONEY_TYPE) { + playMoneyAnim(str); + } else if (textType == NUM_TYPE) { + playNumAnim(str); + } + } + + public void setAnimatorListener(Animator.AnimatorListener animatorListener) { + this.animatorListener = animatorListener; + } + + /** + * 播放金钱数字动画的方法 + * + * @param moneyStr + */ + public void playMoneyAnim(String moneyStr) { + String money = moneyStr.replace(",", "").replace("-", "");//如果传入的数字已经是使用逗号格式化过的,或者含有符号,去除逗号和负号 + try { + BigDecimal bigDecimal = new BigDecimal(money); + float finalFloat = bigDecimal.floatValue(); + if (finalFloat < minMoney) { + //如果传入的为0,则直接使用setText() + setText(moneyStr); + return; + } + ValueAnimator floatAnimator = ValueAnimator.ofObject(new BigDecimalEvaluator(), new BigDecimal(0), bigDecimal); + if (animatorListener != null) { + floatAnimator.addListener(animatorListener); + } + floatAnimator.setDuration(duration); + floatAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + BigDecimal currentNum = (BigDecimal) animation.getAnimatedValue(); + String str = formatter.format(Double.parseDouble(currentNum.toString()));//格式化成两位小数 + // 更新显示的内容 + if (useCommaFormat) { + //使用每三位数字一个逗号的格式 + String formatStr = addComma(str);//三位一个逗号格式的字符串 + setText(formatStr); + } else { + setText(str); + } + } + }); + floatAnimator.start(); + } catch (NumberFormatException e) { + e.printStackTrace(); + this.setText(moneyStr);//如果转换Double失败则直接用setText + } + } + + public String addComma(String str) { + str = new StringBuilder(str).reverse().toString(); //先将字符串颠倒顺序 + if (str.equals("0")) { + return str; + } + String str2 = ""; + for (int i = 0; i < str.length(); i++) { + if (i * 3 + 3 > str.length()) { + str2 += str.substring(i * 3, str.length()); + break; + } + str2 += str.substring(i * 3, i * 3 + 3) + ","; + } + if (str2.endsWith(",")) { + str2 = str2.substring(0, str2.length() - 1); + } + //最后再将顺序反转过来 + String temp = new StringBuilder(str2).reverse().toString(); + //将最后的,去掉 + return temp.substring(0, temp.lastIndexOf(",")) + temp.substring(temp.lastIndexOf(",") + 1, temp.length()); + } + + /** + * 播放数字动画的方法 + * + * @param numStr + */ + public void playNumAnim(String numStr) { + String num = numStr.replace(",", "").replace("-", "");//如果传入的数字已经是使用逗号格式化过的,或者含有符号,去除逗号和负号 + try { + int finalNum = Integer.parseInt(num); + if (finalNum < minNum) { + //由于是整数,每次是递增1,所以如果传入的数字比帧数小,则直接使用setText() + this.setText(numStr); + return; + } + ValueAnimator intAnimator = new ValueAnimator().ofInt(0, finalNum); + if (animatorListener != null) { + intAnimator.addListener(animatorListener); + } + intAnimator.setDuration(duration); + intAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int currentNum = (int) animation.getAnimatedValue(); + setText(String.valueOf(currentNum)); + } + }); + intAnimator.start(); + } catch (NumberFormatException e) { + e.printStackTrace(); + setText(numStr);//如果转换Double失败则直接用setText + } + } +} diff --git a/base/src/main/java/com/heyongrui/base/widget/tickerview/LevenshteinUtils.java b/base/src/main/java/com/heyongrui/base/widget/tickerview/LevenshteinUtils.java new file mode 100644 index 0000000..5cba4e0 --- /dev/null +++ b/base/src/main/java/com/heyongrui/base/widget/tickerview/LevenshteinUtils.java @@ -0,0 +1,212 @@ +package com.heyongrui.base.widget.tickerview; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Created by lambert on 2018/10/11. + */ + +public class LevenshteinUtils { + static final int ACTION_SAME = 0; + static final int ACTION_INSERT = 1; + static final int ACTION_DELETE = 2; + + /** + * This is a wrapper function around {@link #appendColumnActionsForSegment} that + * additionally takes in supportedCharacters. It uses supportedCharacters to compute whether + * the current character should be animated or if it should remain in-place. + *
+ * For specific implementation details, see {@link #appendColumnActionsForSegment}.
+ *
+ * @param source the source char array to animate from
+ * @param target the target char array to animate to
+ * @param supportedCharacters all characters that support custom animation.
+ * @return an int array of size min(source.length, target.length) where each index
+ * corresponds to one of {@link #ACTION_SAME}, {@link #ACTION_INSERT},
+ * {@link #ACTION_DELETE} to represent if we update, insert, or delete a character
+ * at the particular index.
+ */
+ public static int[] computeColumnActions(char[] source, char[] target,
+ Set
+ * For example, given the string "abcde", if the view wants to animate from 'd' to 'b',
+ * it will know that it has to go from 'd' to 'c' to 'b', and these are the characters
+ * that show up during the animation scroll.
+ *
+ * We allow for multiple character lists, and the character lists will be prioritized with
+ * latter lists given a higher priority than the previous lists. e.g. given "123" and "13",
+ * an animation from 1 to 3 will use the sequence [1,3] rather than [1,2,3].
+ *
+ * You can find some helpful character list in {@link TickerUtils}.
+ *
+ * @param characterLists the list of character lists that dictates animation.
+ */
+ public void setCharacterLists(String... characterLists) {
+ columnManager.setCharacterLists(characterLists);
+ if (pendingTextToSet != null) {
+ setText(pendingTextToSet, false);
+ pendingTextToSet = null;
+ }
+ }
+
+ /**
+ * @return whether or not the character lists (via {@link #setCharacterLists}) have been set.
+ * Can use this value to determine if you need to call {@link #setCharacterLists}
+ * before calling {@link #setText}.
+ */
+ public boolean isCharacterListsSet() {
+ return columnManager.getCharacterLists() != null;
+ }
+
+ /**
+ * Sets the string value to display. If the TickerView is currently empty, then this method
+ * will immediately display the provided text. Otherwise, it will run the default animation
+ * to reach the provided text.
+ *
+ * @param text the text to display.
+ */
+ public void setText(String text) {
+ setText(text, !TextUtils.isEmpty(this.text));
+ }
+
+ /**
+ * animate to the provided text or not.
+ *
+ * @param text the text to display.
+ * @param animate whether to animate to text.
+ */
+ public void setText(String text, boolean animate) {
+ if (TextUtils.equals(text, this.text)) {
+ return;
+ }
+
+ this.text = text;
+ final char[] targetText = text == null ? new char[0] : text.toCharArray();
+
+ columnManager.setText(targetText);
+ setContentDescription(text);
+
+ if (animate) {
+ // Kick off the animator that draws the transition
+ if (animator.isRunning()) {
+ animator.cancel();
+ }
+
+ animator.setStartDelay(animationDelayInMillis);
+ animator.setDuration(animationDurationInMillis);
+ animator.setInterpolator(animationInterpolator);
+ animator.start();
+ } else {
+ columnManager.setAnimationProgress(1f);
+ columnManager.onAnimationEnd();
+ checkForRelayout();
+ invalidate();
+ }
+ }
+
+ /**
+ * Get the last set text on the view. This does not equate to the current shown text on the
+ * UI because the animation might not have started or finished yet.
+ *
+ * @return last set text on this view.
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * @return the current text color that's being used to draw the text.
+ */
+ public int getTextColor() {
+ return textColor;
+ }
+
+ /**
+ * Sets the text color used by this view. The default text color is defined by
+ * {@link #DEFAULT_TEXT_COLOR}.
+ *
+ * @param color the color to set the text to.
+ */
+ public void setTextColor(int color) {
+ if (this.textColor != color) {
+ textColor = color;
+ textPaint.setColor(textColor);
+ invalidate();
+ }
+ }
+
+ /**
+ * @return the current text size that's being used to draw the text.
+ */
+ public float getTextSize() {
+ return textSize;
+ }
+
+ /**
+ * Sets the text size used by this view. The default text size is defined by
+ * {@link #DEFAULT_TEXT_SIZE}.
+ *
+ * @param textSize the text size in pixel units.
+ */
+ public void setTextSize(float textSize) {
+ if (this.textSize != textSize) {
+ this.textSize = textSize;
+ textPaint.setTextSize(textSize);
+ onTextPaintMeasurementChanged();
+ }
+ }
+
+ /**
+ * @return the current text typeface.
+ */
+ public Typeface getTypeface() {
+ return textPaint.getTypeface();
+ }
+
+ /**
+ * Sets the typeface size used by this view.
+ *
+ * @param typeface the typeface to use on the text.
+ */
+ public void setTypeface(Typeface typeface) {
+ if (textStyle == Typeface.BOLD_ITALIC) {
+ typeface = Typeface.create(typeface, Typeface.BOLD_ITALIC);
+ } else if (textStyle == Typeface.BOLD) {
+ typeface = Typeface.create(typeface, Typeface.BOLD);
+ } else if (textStyle == Typeface.ITALIC) {
+ typeface = Typeface.create(typeface, Typeface.ITALIC);
+ }
+
+ textPaint.setTypeface(typeface);
+ onTextPaintMeasurementChanged();
+ }
+
+ /**
+ * @return the delay in milliseconds before the transition animations runs
+ */
+ public long getAnimationDelay() {
+ return animationDelayInMillis;
+ }
+
+ /**
+ * Sets the delay in milliseconds before this TickerView runs its transition animations. The
+ * default animation delay is 0.
+ *
+ * @param animationDelayInMillis the delay in milliseconds.
+ */
+ public void setAnimationDelay(long animationDelayInMillis) {
+ this.animationDelayInMillis = animationDelayInMillis;
+ }
+
+ /**
+ * @return the duration in milliseconds that the transition animations run for.
+ */
+ public long getAnimationDuration() {
+ return animationDurationInMillis;
+ }
+
+ /**
+ * Sets the duration in milliseconds that this TickerView runs its transition animations. The
+ * default animation duration is defined by {@link #DEFAULT_ANIMATION_DURATION}.
+ *
+ * @param animationDurationInMillis the duration in milliseconds.
+ */
+ public void setAnimationDuration(long animationDurationInMillis) {
+ this.animationDurationInMillis = animationDurationInMillis;
+ }
+
+ /**
+ * @return the interpolator used to interpolate the animated values.
+ */
+ public Interpolator getAnimationInterpolator() {
+ return animationInterpolator;
+ }
+
+ /**
+ * Sets the interpolator for the transition animation. The default interpolator is defined by
+ * {@link #DEFAULT_ANIMATION_INTERPOLATOR}.
+ *
+ * @param animationInterpolator the interpolator for the animation.
+ */
+ public void setAnimationInterpolator(Interpolator animationInterpolator) {
+ this.animationInterpolator = animationInterpolator;
+ }
+
+ /**
+ * @return the current text gravity used to align the text. Should be one of the values defined
+ * in {@link Gravity}.
+ */
+ public int getGravity() {
+ return gravity;
+ }
+
+ /**
+ * Sets the gravity used to align the text.
+ *
+ * @param gravity the new gravity, should be one of the values defined in
+ * {@link Gravity}.
+ */
+ public void setGravity(int gravity) {
+ if (this.gravity != gravity) {
+ this.gravity = gravity;
+ invalidate();
+ }
+ }
+
+ /**
+ * Enables/disables the flag to animate measurement changes. If this flag is enabled, any
+ * animation that changes the content's text width (e.g. 9999 to 10000) will have the view's
+ * measured width animated along with the text width. However, a side effect of this is that
+ * the entering/exiting character might get truncated by the view's view bounds as the width
+ * shrinks or expands.
+ *
+ * Warning: using this feature may degrade performance as it will force a re-measure and
+ * re-layout during each animation frame.
+ *
+ * This flag is disabled by default.
+ *
+ * @param animateMeasurementChange whether or not to animate measurement changes.
+ */
+ public void setAnimateMeasurementChange(boolean animateMeasurementChange) {
+ this.animateMeasurementChange = animateMeasurementChange;
+ }
+
+ /**
+ * @return whether or not we are currently animating measurement changes.
+ */
+ public boolean getAnimateMeasurementChange() {
+ return animateMeasurementChange;
+ }
+
+ /**
+ * Adds a custom {@link Animator.AnimatorListener} to listen to animator
+ * update events used by this view.
+ *
+ * @param animatorListener the custom animator listener.
+ */
+ public void addAnimatorListener(Animator.AnimatorListener animatorListener) {
+ animator.addListener(animatorListener);
+ }
+
+ /**
+ * Removes the specified custom {@link Animator.AnimatorListener} from
+ * this view.
+ *
+ * @param animatorListener the custom animator listener.
+ */
+ public void removeAnimatorListener(Animator.AnimatorListener animatorListener) {
+ animator.removeListener(animatorListener);
+ }
+
+
+ /********** END PUBLIC API **********/
+
+
+ /**
+ * Force the view to call {@link #requestLayout()} if the new text doesn't match the old bounds
+ * we set for the previous view state.
+ */
+ private void checkForRelayout() {
+ final boolean widthChanged = lastMeasuredDesiredWidth != computeDesiredWidth();
+ final boolean heightChanged = lastMeasuredDesiredHeight != computeDesiredHeight();
+
+ if (widthChanged || heightChanged) {
+ requestLayout();
+ }
+ }
+
+ private int computeDesiredWidth() {
+ final int contentWidth = (int) (animateMeasurementChange ?
+ columnManager.getCurrentWidth() : columnManager.getMinimumRequiredWidth());
+ return contentWidth + getPaddingLeft() + getPaddingRight();
+ }
+
+ private int computeDesiredHeight() {
+ return (int) metrics.getCharHeight() + getPaddingTop() + getPaddingBottom();
+ }
+
+ /**
+ * Re-initialize all of our variables that are dependent on the TextPaint measurements.
+ */
+ private void onTextPaintMeasurementChanged() {
+ metrics.invalidate();
+ checkForRelayout();
+ invalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ lastMeasuredDesiredWidth = computeDesiredWidth();
+ lastMeasuredDesiredHeight = computeDesiredHeight();
+
+ int desiredWidth = resolveSize(lastMeasuredDesiredWidth, widthMeasureSpec);
+ int desiredHeight = resolveSize(lastMeasuredDesiredHeight, heightMeasureSpec);
+
+ setMeasuredDimension(desiredWidth, desiredHeight);
+ }
+
+ @Override
+ protected void onSizeChanged(int width, int height, int oldw, int oldh) {
+ super.onSizeChanged(width, height, oldw, oldh);
+ viewBounds.set(getPaddingLeft(), getPaddingTop(), width - getPaddingRight(),
+ height - getPaddingBottom());
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.save();
+
+ realignAndClipCanvasForGravity(canvas);
+
+ // canvas.drawText writes the text on the baseline so we need to translate beforehand.
+ canvas.translate(0f, metrics.getCharBaseline());
+
+ columnManager.draw(canvas, textPaint);
+
+ canvas.restore();
+ }
+
+ private void realignAndClipCanvasForGravity(Canvas canvas) {
+ final float currentWidth = columnManager.getCurrentWidth();
+ final float currentHeight = metrics.getCharHeight();
+ realignAndClipCanvasForGravity(canvas, gravity, viewBounds, currentWidth, currentHeight);
+ }
+
+ // VisibleForTesting
+ static void realignAndClipCanvasForGravity(Canvas canvas, int gravity, Rect viewBounds,
+ float currentWidth, float currentHeight) {
+ final int availableWidth = viewBounds.width();
+ final int availableHeight = viewBounds.height();
+
+ float translationX = 0;
+ float translationY = 0;
+ if ((gravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
+ translationY = viewBounds.top + (availableHeight - currentHeight) / 2f;
+ }
+ if ((gravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
+ translationX = viewBounds.left + (availableWidth - currentWidth) / 2f;
+ }
+ if ((gravity & Gravity.TOP) == Gravity.TOP) {
+ translationY = 0;
+ }
+ if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ translationY = viewBounds.top + (availableHeight - currentHeight);
+ }
+ if ((gravity & Gravity.START) == Gravity.START) {
+ translationX = 0;
+ }
+ if ((gravity & Gravity.END) == Gravity.END) {
+ translationX = viewBounds.left + (availableWidth - currentWidth);
+ }
+
+ canvas.translate(translationX, translationY);
+ canvas.clipRect(0f, 0f, currentWidth, currentHeight);
+ }
+}
diff --git a/main/src/main/java/com/heyongrui/main/HomeFragment.java b/main/src/main/java/com/heyongrui/main/HomeFragment.java
index 91417f4..46fe96d 100644
--- a/main/src/main/java/com/heyongrui/main/HomeFragment.java
+++ b/main/src/main/java/com/heyongrui/main/HomeFragment.java
@@ -1,12 +1,25 @@
package com.heyongrui.main;
import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
import com.alibaba.android.arouter.launcher.ARouter;
import com.heyongrui.base.assist.ConfigConstants;
import com.heyongrui.base.base.BaseFragment;
+import com.heyongrui.base.widget.numberruntextview.NumberRunningTextView;
+import com.heyongrui.base.widget.tickerview.TickerUtils;
+import com.heyongrui.base.widget.tickerview.TickerView;
-public class HomeFragment extends BaseFragment {
+import java.util.Random;
+
+public class HomeFragment extends BaseFragment implements View.OnClickListener {
+
+
+ private TickerView tickerView;
+ private NumberRunningTextView numberRunTv;
+
+ private int tickerCount;
public static HomeFragment getInstance() {
HomeFragment fragment = new HomeFragment();
@@ -23,17 +36,49 @@ protected int getLayoutId() {
}
@Override
- protected void initView(Bundle savedInstanceState) {
- addOnClickListeners(view -> {
- int id = view.getId();
- if (id == R.id.tv_home) {
- ARouter.getInstance().build(ConfigConstants.PATH_KOTLIN).navigation();
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.tv_home) {
+ ARouter.getInstance().build(ConfigConstants.PATH_KOTLIN).navigation();
+ } else if (id == R.id.ticker_view) {
+ if (tickerCount < 5) {
+ tickerView.setCharacterLists(TickerUtils.provideNumberList());
+ tickerView.setText("¥" + new Random().nextInt(500) + "." + new Random().nextInt(100));
+ } else if (tickerCount < 10 && tickerCount >= 5) {
+ tickerView.setCharacterLists(TickerUtils.provideAlphabeticalList());
+ int digits = new Random().nextInt(2) + 6;
+ tickerView.setText(generateChars(new Random(), TickerUtils.provideAlphabeticalList(), digits));
+ }
+ if (tickerCount == 10) {
+ tickerCount = 0;
+ } else {
+ tickerCount++;
}
- }, R.id.tv_home);
+ } else if (id == R.id.number_run_tv) {
+ numberRunTv.setContent(new Random().nextInt(500) + ".47");
+ }
+ }
+
+ @Override
+ protected void initView(Bundle savedInstanceState) {
+ tickerView = mView.findViewById(R.id.ticker_view);
+ numberRunTv = mView.findViewById(R.id.number_run_tv);
+ TextView tvHome = mView.findViewById(R.id.tv_home);
+
+ addOnClickListeners(this, tvHome);
}
@Override
protected void initData(Bundle savedInstanceState) {
}
+
+ private String generateChars(Random random, String list, int numDigits) {
+ final char[] result = new char[numDigits];
+ for (int i = 0; i < numDigits; i++) {
+ result[i] = list.charAt(random.nextInt(list.length()));
+ }
+ return new String(result);
+ }
+
}
diff --git a/main/src/main/java/com/heyongrui/main/IHomeFragmentProvider.java b/main/src/main/java/com/heyongrui/main/provider/IHomeFragmentProvider.java
similarity index 87%
rename from main/src/main/java/com/heyongrui/main/IHomeFragmentProvider.java
rename to main/src/main/java/com/heyongrui/main/provider/IHomeFragmentProvider.java
index 393125a..40b92cb 100644
--- a/main/src/main/java/com/heyongrui/main/IHomeFragmentProvider.java
+++ b/main/src/main/java/com/heyongrui/main/provider/IHomeFragmentProvider.java
@@ -1,4 +1,4 @@
-package com.heyongrui.main;
+package com.heyongrui.main.provider;
import android.content.Context;
@@ -6,6 +6,7 @@
import com.heyongrui.base.assist.ConfigConstants;
import com.heyongrui.base.base.BaseFragment;
import com.heyongrui.base.provider.IFragmentProvider;
+import com.heyongrui.main.HomeFragment;
@Route(path = ConfigConstants.PATH_HOME_PROVIDER)
public class IHomeFragmentProvider implements IFragmentProvider {
diff --git a/main/src/main/res/layout/fragment_home.xml b/main/src/main/res/layout/fragment_home.xml
index a338cae..8ada212 100644
--- a/main/src/main/res/layout/fragment_home.xml
+++ b/main/src/main/res/layout/fragment_home.xml
@@ -4,6 +4,33 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+ *
+ *
+ * @param context context from constructor
+ * @param attrs attrs from constructor
+ * @param defStyleAttr defStyleAttr from constructor
+ * @param defStyleRes defStyleRes from constructor
+ */
+ protected void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ final Resources res = context.getResources();
+ final StyledAttributes styledAttributes = new StyledAttributes(res);
+
+ // Set the view attributes from XML or from default values defined in this class
+ final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.TickerView,
+ defStyleAttr, defStyleRes);
+
+ final int textAppearanceResId = arr.getResourceId(
+ R.styleable.TickerView_android_textAppearance, -1);
+
+ // Check textAppearance first
+ if (textAppearanceResId != -1) {
+ final TypedArray textAppearanceArr = context.obtainStyledAttributes(
+ textAppearanceResId, R.styleable.TickerView);
+ styledAttributes.applyTypedArray(textAppearanceArr);
+ textAppearanceArr.recycle();
+ }
+
+ // Custom set attributes on the view should override textAppearance if applicable.
+ styledAttributes.applyTypedArray(arr);
+
+ // After we've fetched the correct values for the attributes, set them on the view
+ animationInterpolator = DEFAULT_ANIMATION_INTERPOLATOR;
+ this.animationDurationInMillis = arr.getInt(
+ R.styleable.TickerView_ticker_animationDuration, DEFAULT_ANIMATION_DURATION);
+ this.animateMeasurementChange = arr.getBoolean(
+ R.styleable.TickerView_ticker_animateMeasurementChange, false);
+ this.gravity = styledAttributes.gravity;
+
+ if (styledAttributes.shadowColor != 0) {
+ textPaint.setShadowLayer(styledAttributes.shadowRadius, styledAttributes.shadowDx,
+ styledAttributes.shadowDy, styledAttributes.shadowColor);
+ }
+ if (styledAttributes.textStyle != 0) {
+ textStyle = styledAttributes.textStyle;
+ setTypeface(textPaint.getTypeface());
+ }
+
+ setTextColor(styledAttributes.textColor);
+ setTextSize(styledAttributes.textSize);
+
+ final int defaultCharList =
+ arr.getInt(R.styleable.TickerView_ticker_defaultCharacterList, 0);
+ switch (defaultCharList) {
+ case 1:
+ setCharacterLists(TickerUtils.provideNumberList());
+ break;
+ case 2:
+ setCharacterLists(TickerUtils.provideAlphabeticalList());
+ break;
+ default:
+ if (isInEditMode()) {
+ setCharacterLists(TickerUtils.provideNumberList());
+ }
+ }
+
+ if (isCharacterListsSet()) {
+ setText(styledAttributes.text, false);
+ } else {
+ this.pendingTextToSet = styledAttributes.text;
+ }
+
+ arr.recycle();
+
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ columnManager.setAnimationProgress(
+ animation.getAnimatedFraction());
+ checkForRelayout();
+ invalidate();
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ columnManager.onAnimationEnd();
+ checkForRelayout();
+ invalidate();
+ }
+ });
+ }
+
+ /**
+ * Only attributes that can be applied from `android:textAppearance` should be added here.
+ */
+ private class StyledAttributes {
+ int gravity;
+ int shadowColor;
+ float shadowDx;
+ float shadowDy;
+ float shadowRadius;
+ String text;
+ int textColor;
+ float textSize;
+ int textStyle;
+
+ StyledAttributes(Resources res) {
+ textColor = DEFAULT_TEXT_COLOR;
+ textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ DEFAULT_TEXT_SIZE, res.getDisplayMetrics());
+ gravity = DEFAULT_GRAVITY;
+ }
+
+ void applyTypedArray(TypedArray arr) {
+ gravity = arr.getInt(R.styleable.TickerView_android_gravity, gravity);
+ shadowColor = arr.getColor(R.styleable.TickerView_android_shadowColor,
+ shadowColor);
+ shadowDx = arr.getFloat(R.styleable.TickerView_android_shadowDx, shadowDx);
+ shadowDy = arr.getFloat(R.styleable.TickerView_android_shadowDy, shadowDy);
+ shadowRadius = arr.getFloat(R.styleable.TickerView_android_shadowRadius,
+ shadowRadius);
+ text = arr.getString(R.styleable.TickerView_android_text);
+ textColor = arr.getColor(R.styleable.TickerView_android_textColor, textColor);
+ textSize = arr.getDimension(R.styleable.TickerView_android_textSize, textSize);
+ textStyle = arr.getInt(R.styleable.TickerView_android_textStyle, textStyle);
+ }
+ }
+
+
+ /********** BEGIN PUBLIC API **********/
+
+
+ /**
+ * This is the primary class that Ticker uses to determine how to animate from one character
+ * to another. The provided strings dictates what characters will appear between
+ * the start and end characters.
+ *