activity_main
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"> <angi.conghuy.com.myapplication.MultiScrollNumber
android:id="@+id/scroll_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" /> <EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" /> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/editText"
android:layout_centerHorizontal="true"
android:orientation="horizontal"> <Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Click" /> <Button
android:id="@+id/btnCustom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="CustomFont" /> <Button
android:id="@+id/btnColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SetColor" /> </LinearLayout> </RelativeLayout>
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button button, btnCustom, btnColor; private MultiScrollNumber scrollNumber; @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); button = (Button) findViewById(R.id.button); btnCustom = (Button) findViewById(R.id.btnCustom); btnColor = (Button) findViewById(R.id.btnColor); button.setOnClickListener(this); btnCustom.setOnClickListener(this); btnColor.setOnClickListener(this); scrollNumber = (MultiScrollNumber) findViewById(R.id.scroll_number); scrollNumber.setNumber(2048); } @Override
public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } int a = 2048; @Override
public void onClick(View v) { if (v == button) { a += 1024; scrollNumber.setNumber(a); } else if (v == btnColor) { scrollNumber.setTextColors(new int[]{R.color.blue01, R.color.red01, R.color.green01, R.color.purple01}); } else if (v == btnCustom) { scrollNumber.setTextFont("BROADW.TTF"); } } }
MultiScrollNumber
public class MultiScrollNumber extends LinearLayout { private Context mContext; private List<Integer> mTargetNumbers = new ArrayList<>(); private List<Integer> mPrimaryNumbers = new ArrayList<>(); private List<ScrollNumber> mScrollNumbers = new ArrayList<>(); private int mTextSize = 130; private int[] mTextColors = new int[]{R.color.purple01}; private Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); private String mFontFileName; public MultiScrollNumber(Context context) { this(context, null); } public MultiScrollNumber(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MultiScrollNumber(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.MultiScrollNumber); int primaryNumber = typedArray.getInteger(R.styleable.MultiScrollNumber_primary_number, 0); int targetNumber = typedArray.getInteger(R.styleable.MultiScrollNumber_target_number, 0); int numberSize = typedArray.getInteger(R.styleable.MultiScrollNumber_number_size, 100); setNumber(primaryNumber, targetNumber); setTextSize(numberSize); typedArray.recycle(); setOrientation(HORIZONTAL); setGravity(Gravity.CENTER); } public void setNumber(int val) { resetView(); int number = val; while (number > 0) { int i = number % 10; mTargetNumbers.add(i); number /= 10; } for (int i = mTargetNumbers.size() - 1; i >= 0; i--) { ScrollNumber scrollNumber = new ScrollNumber(mContext); scrollNumber.setTextColor(ContextCompat .getColor(mContext, mTextColors[i % mTextColors.length])); scrollNumber.setTextSize(mTextSize); scrollNumber.setInterpolator(mInterpolator); if (!TextUtils.isEmpty(mFontFileName)) scrollNumber.setTextFont(mFontFileName); scrollNumber.setNumber(0, mTargetNumbers.get(i), i * 10); mScrollNumbers.add(scrollNumber); addView(scrollNumber); } } private void resetView() { mTargetNumbers.clear(); mScrollNumbers.clear(); removeAllViews(); } public void setNumber(int from, int to) { if (to < from) throw new UnsupportedOperationException("'to' value must > 'from' value"); resetView(); // operate to int number = to, count = 0; while (number > 0) { int i = number % 10; mTargetNumbers.add(i); number /= 10; count++; } // operate from number = from; while (count > 0) { int i = number % 10; mPrimaryNumbers.add(i); number /= 10; count--; } for (int i = mTargetNumbers.size() - 1; i >= 0; i--) { ScrollNumber scrollNumber = new ScrollNumber(mContext); scrollNumber.setTextColor(ContextCompat .getColor(mContext, mTextColors[i % mTextColors.length])); scrollNumber.setTextSize(mTextSize); if (!TextUtils.isEmpty(mFontFileName)) scrollNumber.setTextFont(mFontFileName); scrollNumber.setNumber(mPrimaryNumbers.get(i), mTargetNumbers.get(i), i * 10); mScrollNumbers.add(scrollNumber); addView(scrollNumber); } } public void setTextColors(@ColorRes int[] textColors) { if (textColors == null || textColors.length == 0) throw new IllegalArgumentException("color array couldn't be empty!"); mTextColors = textColors; for (int i = mScrollNumbers.size() - 1; i >= 0; i--) { ScrollNumber scrollNumber = mScrollNumbers.get(i); scrollNumber.setTextColor(ContextCompat .getColor(mContext, mTextColors[i % mTextColors.length])); } } public void setTextSize(int textSize) { if (textSize <= 0) throw new IllegalArgumentException("text size must > 0!"); mTextSize = textSize; for (ScrollNumber s : mScrollNumbers) { s.setTextSize(textSize); } } public void setInterpolator(Interpolator interpolator) { if (interpolator == null) throw new IllegalArgumentException("interpolator couldn't be null"); mInterpolator = interpolator; for (ScrollNumber s : mScrollNumbers) { s.setInterpolator(interpolator); } } public void setTextFont(String fileName) { if (TextUtils.isEmpty(fileName)) throw new IllegalArgumentException("file name is null"); mFontFileName = fileName; for (ScrollNumber s : mScrollNumbers) { s.setTextFont(fileName); } } }
ScrollNumber
public class ScrollNumber extends View {
private int mDeltaNum;
private int mCurNum;
private int mNextNum;
private int mTargetNum;
private Context mContext;
private float mOffset;
private Paint mPaint;
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
private int mTextCenterX;
private int mTextHeight;
private Rect mTextBounds = new Rect();
private int mTextSize = sp2px(130);
private int mTextColor = 0xFF000000;
private Typeface mTypeface;
public ScrollNumber(Context context) {
this(context, null);
}
public ScrollNumber(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrollNumber(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
if (mTypeface != null) mPaint.setTypeface(mTypeface);
measureTextHeight();
// setNumber(0, 6, 1000);
}
public void setNumber(final int from, final int to, long delay) {
postDelayed(new Runnable() {
@Override
public void run() {
setFromNumber(from);
setTargetNumber(to);
mDeltaNum = to - from;
}
}, delay);
}
public void setTextSize(int textSize) {
this.mTextSize = sp2px(textSize);
mPaint.setTextSize(mTextSize);
measureTextHeight();
requestLayout();
invalidate();
}
public void setTextFont(String fileName) {
if (TextUtils.isEmpty(fileName))
throw new IllegalArgumentException("please check file name end with '.ttf' or '.otf'");
mTypeface = Typeface.createFromAsset(mContext.getAssets(), fileName);
if (mTypeface == null) throw new RuntimeException("please check your font!");
mPaint.setTypeface(mTypeface);
requestLayout();
invalidate();
}
public void setTextColor(int mTextColor) {
this.mTextColor = mTextColor;
mPaint.setColor(mTextColor);
invalidate();
}
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
}
private void measureTextHeight() {
mPaint.getTextBounds(mCurNum + "", 0, 1, mTextBounds);
mTextHeight = mTextBounds.height();
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
mTextCenterX = (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / 2;
}
private int measureHeight(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int val = MeasureSpec.getSize(measureSpec);
int result = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
result = val;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
mPaint.getTextBounds("0", 0, 1, mTextBounds);
result = mTextBounds.height();
break;
}
result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
return result + getPaddingTop() + getPaddingBottom()+dp2px(40);
}
private int measureWidth(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int val = MeasureSpec.getSize(measureSpec);
int result = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
result = val;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
mPaint.getTextBounds("0", 0, 1, mTextBounds);
result = mTextBounds.width();
break;
}
result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
return result + getPaddingLeft() + getPaddingRight() + 15;
}
@Override protected void onDraw(Canvas canvas) {
if (mCurNum != mTargetNum) {
postDelayed(mScrollRunnable, 0);
if (mOffset <= -1) {
mOffset = 0;
calNum(mCurNum + 1);
}
}
canvas.translate(0, mOffset * getMeasuredHeight());
drawSelf(canvas);
drawNext(canvas);
// canvas.restore(); }
private void setFromNumber(int number) {
if (number < 0 || number > 9)
throw new RuntimeException("invalidate number , should in [0,9]");
calNum(number);
mOffset = 0;
invalidate();
}
private void calNum(int number) {
number = number == -1 ? 9 : number;
number = number == 10 ? 0 : number;
mCurNum = number;
mNextNum = number + 1 == 10 ? 0 : number + 1;
}
private Runnable mScrollRunnable = new Runnable() {
@Override
public void run() {
float x = (float) (1 - 1.0 * (mTargetNum - mCurNum) / mDeltaNum);
mOffset -= 0.15f * (1 - mInterpolator.getInterpolation(x) + 0.1);
invalidate();
}
};
private void drawNext(Canvas canvas) {
int y = getMeasuredHeight() * 3 / 2;
canvas.drawText(mNextNum + "", mTextCenterX, y + mTextHeight / 2,
mPaint);
}
private void drawSelf(Canvas canvas) {
int y = getMeasuredHeight() / 2;
canvas.drawText(mCurNum + "", mTextCenterX, y + mTextHeight / 2, mPaint);
}
public void setTargetNumber(int nextNum) {
this.mTargetNum = nextNum;
invalidate();
}
private int dp2px(float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}
private int sp2px(float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
dpVal, getResources().getDisplayMetrics());
}
}
Tài liệu hữu ích đó bạn ! Tuy nhiên code nếu bạn comment một số chỗ quan trọng hoặc video có 1 vài description thì tốt quá ! Thanks for share :D
ReplyDelete