Android滚动显示数字动画

项目中需要做一个数字滚动的特效,完成后特意记录下来,方便他人。。。
先上效果图:
滚动显示数字动画

以下为主要实行代码:
ScrollPickerView.class

/**
 * 滚动选择器,带惯性滑动
 */
public abstract class ScrollPickerView<T> extends View {

    private int visibleItemCount = 3; // 可见的item数量

    private boolean isInertiaScroll = true; // 快速滑动时是否惯性滚动一段距离,默认开启
    private boolean isCirculation = true; // 是否循环滚动,默认开启

    // 不允许父组件拦截触摸事件,设置为true为不允许拦截,此时该设置才生效
    // 当嵌入到ScrollView等滚动组件中,为了使该自定义滚动选择器可以正常工作,请设置为true
    private boolean disallowInterceptTouch = false;

    private int selected; // 当前选中的item下标
    private List<T> data;
    private int itemHeight = 0; // 每个条目的高度,当垂直滚动时,高度=mMeasureHeight/visibleItemCount
    private int itemWidth = 0; // 每个条目的宽度,当水平滚动时,宽度=mMeasureWidth/visibleItemCount
    private int itemSize; // 当垂直滚动时,itemSize = itemHeight;水平滚动时,itemSize = itemWidth
    private int centerPosition = -1; // 中间item的位置,0<=centerPosition<visibleItemCount,默认为 visibleItemCount / 2
    private int centerY; // 中间item的起始坐标y(不考虑偏移),当垂直滚动时,y= centerPosition*itemHeight
    private int centerX; // 中间item的起始坐标x(不考虑偏移),当垂直滚动时,x = centerPosition*itemWidth
    private int centerPoint; // 当垂直滚动时,centerPoint = centerY;水平滚动时,centerPoint = centerX
    private float lastMoveY; // 触摸的坐标y
    private float lastMoveX; // 触摸的坐标X

    private float moveLength = 0; // item移动长度,负数表示向上移动,正数表示向下移动

    private GestureDetector gestureDetector;
    private OnSelectedListener listener;

    private Scroller scroller;
    private boolean isFling; // 是否正在惯性滑动
    private boolean isMovingCenter; // 是否正在滑向中间

    // 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次触屏滑动的坐标
    private int lastScrollY = 0; // Scroller的坐标y
    private int lastScrollX = 0; // Scroller的坐标x

    private boolean disallowTouch = false; // 不允许触摸

    private Paint paint;
    private Drawable centerItemBackground = null; // 中间选中item的背景色

    private boolean canTap = true; // 单击切换选项或触发点击监听器

    private boolean isHorizontal = false; // 是否水平滚动

    private boolean drawAllItem = false; // 是否绘制每个item(包括在边界外的item)

    private boolean isAutoScrolling = false;
    private ValueAnimator autoScrollAnimator;
    private final static SlotInterpolator autoScrollInterpolator = new SlotInterpolator();
    private int defaultValue = -1;

    public ScrollPickerView(Context context) {
        super(context);
        this.initView(context);
        return;
    }

    public ScrollPickerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initView(context);
        return;
    }

    public ScrollPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.initView(context);
        return;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ScrollPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.initView(context);
        return;
    }

    /**
     * 初始化视图
     *
     * @param context
     */
    private void initView(Context context) {
        this.gestureDetector = new GestureDetector(getContext(), new FlingOnGestureListener());
        this.scroller = new Scroller(getContext());
        this.autoScrollAnimator = ValueAnimator.ofInt(0, 0);

        this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        this.paint.setStyle(Paint.Style.FILL);
        return;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (this.data == null || this.data.size() <= 0) {
            return;
        }

        // 选中item的背景色
        if (this.centerItemBackground != null) {
            this.centerItemBackground.draw(canvas);
        }

        // 只绘制可见的item
        int length = Math.max(this.centerPosition + 1, this.visibleItemCount - this.centerPosition);
        int position;
        int start = Math.min(length, this.data.size());
        if (this.drawAllItem) {
            start = this.data.size();
        }
        // 上下两边
        for (int i = start; i >= 1; i--) { // 先从远离中间位置的item绘制,当item内容偏大时,较近的item覆盖在较远的上面

            if (this.drawAllItem || i <= this.centerPosition + 1) {  // 上面的items,相对位置为 -i
                position = this.selected - i < 0 ? this.data.size() + this.selected - i : this.selected - i;
                // 传入位置信息,绘制item
                if (this.isCirculation) {
                    this.drawItem(canvas, this.data, position, -i, this.moveLength, this.centerPoint + this.moveLength - i * this.itemSize);
                } else if (selected - i >= 0) { // 非循环滚动
                    this.drawItem(canvas, this.data, position, -i, this.moveLength, this.centerPoint + this.moveLength - i * this.itemSize);
                }
            }
            if (this.drawAllItem || i <= this.visibleItemCount - this.centerPosition) {  // 下面的items,相对位置为 i
                position = this.selected + i >= this.data.size() ? this.selected + i - this.data.size() : this.selected + i;
                // 传入位置信息,绘制item
                if (this.isCirculation) {
                    this.drawItem(canvas, this.data, position, i, this.moveLength, this.centerPoint + this.moveLength + i * this.itemSize);
                } else if (selected + i < data.size()) { // 非循环滚动
                    this.drawItem(canvas, this.data, position, i, this.moveLength, this.centerPoint + this.moveLength + i * this.itemSize);
                }
            }
        }

        // 选中的item
        this.drawItem(canvas, this.data, this.selected, 0, this.moveLength, this.centerPoint + this.moveLength);

        return;
    }

    /**
     * 绘制item
     *
     * @param canvas
     * @param data        数据集
     * @param position   在data数据集中的位置
     * @param relative   相对中间item的位置,relative==0表示中间item,relative<0表示上(左)边的item,relative>0表示下(右)边的item
     * @param moveLength 中间item滚动的距离,moveLength<0则表示向上(右)滚动的距离,moveLength>0则表示向下(左)滚动的距离
     * @param top        当前绘制item的坐标,当垂直滚动时为顶部y的坐标;当水平滚动时为item最左边x的坐标
     */
    public abstract void drawItem(Canvas canvas, List<T> data, int position, int relative, float moveLength, float top);

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        return;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        this.reset();
        return;
    }

    private void reset() {
        if (this.centerPosition < 0) {
            this.centerPosition = this.visibleItemCount / 2;
        }

        if (this.isHorizontal) {
            this.itemHeight = this.getMeasuredHeight();
            this.itemWidth = this.getMeasuredWidth() / this.visibleItemCount;

            this.centerY = 0;
            this.centerX = this.centerPosition * this.itemWidth;

            this.itemSize = this.itemWidth;
            this.centerPoint = this.centerX;
        } else {
            this.itemHeight = this.getMeasuredHeight() / this.visibleItemCount;
            this.itemWidth = this.getMeasuredWidth();

            this.centerY = this.centerPosition * this.itemHeight;
            this.centerX = 0;

            this.itemSize = this.itemHeight;
            this.centerPoint = this.centerY;
        }

        if (this.centerItemBackground != null) {
            this.centerItemBackground.setBounds(this.centerX, this.centerY, this.centerX + this.itemWidth, this.centerY + this.itemHeight);
        }
        return;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.disallowTouch) { // 不允许触摸
            return true;
        }

        if (this.gestureDetector.onTouchEvent(event)) {
            return true;
        }

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_MOVE:

                if (this.isHorizontal) {
                    if (Math.abs(event.getX() - this.lastMoveX) < 0.1f) {
                        return true;
                    }
                    this.moveLength += event.getX() - this.lastMoveX;
                } else {
                    if (Math.abs(event.getY() - this.lastMoveY) < 0.1f) {
                        return true;
                    }
                    this.moveLength += event.getY() - this.lastMoveY;
                }
                this.lastMoveY = event.getY();
                this.lastMoveX = event.getX();
                this.checkCirculation();
                this.invalidate();
                break;
            case MotionEvent.ACTION_UP:
                this.lastMoveY = event.getY();
                this.lastMoveX = event.getX();
                this.moveToCenter();
                break;
        }
        return true;
    }

    /**
     * @param curr
     * @param end
     */
    private void computeScroll(int curr, int end, float rate) {
        if (rate < 1) { // 正在滚动
            if (this.isHorizontal) {
                // 可以把scroller看做模拟的触屏滑动操作,mLastScrollX为上次滑动的坐标
                this.moveLength = this.moveLength + curr - this.lastScrollX;
                this.lastScrollX = curr;
            } else {
                // 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次滑动的坐标
                this.moveLength = this.moveLength + curr - this.lastScrollY;
                this.lastScrollY = curr;
            }
            this.checkCirculation();
            this.invalidate();
        } else { // 滚动完毕
            this.isMovingCenter = false;
            this.lastScrollY = 0;
            this.lastScrollX = 0;

            // 直接居中,不通过动画
            if (this.moveLength > 0) {  向下滑动
                if (this.moveLength < this.itemSize / 2) {
                    this.moveLength = 0;
                } else {
                    this.moveLength = this.itemSize;
                }
            } else {
                if (-this.moveLength < this.itemSize / 2) {
                    this.moveLength = 0;
                } else {
                    this.moveLength = -this.itemSize;
                }
            }
            this.checkCirculation();
            this.moveLength = 0;
            this.lastScrollY = 0;
            this.lastScrollX = 0;
            this.notifySelected();
            this.invalidate();
        }
        return;
    }

    @Override
    public void computeScroll() {
        if (this.scroller.computeScrollOffset()) { // 正在滚动
            if (this.isHorizontal) {
                // 可以把scroller看做模拟的触屏滑动操作,mLastScrollX为上次滑动的坐标
                this.moveLength = this.moveLength + this.scroller.getCurrX() - this.lastScrollX;
            } else {
                // 可以把scroller看做模拟的触屏滑动操作,mLastScrollY为上次滑动的坐标
                this.moveLength = this.moveLength + this.scroller.getCurrY() - this.lastScrollY;
            }
            this.lastScrollY = this.scroller.getCurrY();
            this.lastScrollX = this.scroller.getCurrX();
            this.checkCirculation(); // 检测当前选中的item
            this.invalidate();
        } else { // 滚动完毕
            if (this.isFling) {
                this.isFling = false;
                this.moveToCenter(); // 滚动到中间位置
            } else if (this.isMovingCenter) { // 选择完成,回调给监听器
                this.moveLength = 0;
                this.isMovingCenter = false;
                this.lastScrollY = 0;
                this.lastScrollX = 0;
                this.notifySelected();
            }
        }
        return;
    }

    public void cancelScroll() {
        this.lastScrollY = 0;
        this.lastScrollX = 0;
        this.isFling = false;
        this.isMovingCenter = false;
        this.scroller.abortAnimation();
        this.stopAutoScroll();
        return;
    }

    // 检测当前选择的item位置
    private void checkCirculation() {
        if (this.moveLength >= this.itemSize) { // 向下滑动
            // 该次滚动距离中越过的item数量
            int span = (int) (this.moveLength / this.itemSize);
            this.selected -= span;
            if (this.selected < 0) {  // 滚动顶部,判断是否循环滚动
                if (this.isCirculation) {
                    do {
                        this.selected = this.data.size() + this.selected;
                    } while (this.selected < 0); // 当越过的item数量超过一圈时
                    this.moveLength = (this.moveLength - this.itemSize) % this.itemSize;
                } else { // 非循环滚动
                    this.selected = 0;
                    this.moveLength = this.itemSize;
                    if (this.isFling) { // 停止惯性滑动,根据computeScroll()中的逻辑,下一步将调用moveToCenter()
                        this.scroller.forceFinished(true);
                    }
                    if (this.isMovingCenter) { //  移回中间位置
                        this.scroll(this.moveLength, 0);
                    }
                }
            } else {
                this.moveLength = (this.moveLength - this.itemSize) % this.itemSize;
            }

        } else if (this.moveLength <= -this.itemSize) { // 向上滑动
            // 该次滚动距离中越过的item数量
            int span = (int) (-this.moveLength / this.itemSize);
            this.selected += span;
            if (this.selected >= this.data.size()) { // 滚动末尾,判断是否循环滚动
                if (this.isCirculation) {
                    do {
                        this.selected = this.selected - this.data.size();
                    } while (this.selected >= this.data.size()); // 当越过的item数量超过一圈时
                    this.moveLength = (this.moveLength + this.itemSize) % this.itemSize;
                } else { // 非循环滚动
                    this.selected = this.data.size() - 1;
                    this.moveLength = -this.itemSize;
                    if (this.isFling) { // 停止惯性滑动,根据computeScroll()中的逻辑,下一步将调用moveToCenter()
                        this.scroller.forceFinished(true);
                    }
                    if (this.isMovingCenter) { //  移回中间位置
                        this.scroll(this.moveLength, 0);
                    }
                }
            } else {
                this.moveLength = (this.moveLength + this.itemSize) % this.itemSize;
            }
        }
        return;
    }

    // 移动到中间位置
    private void moveToCenter() {

        if (!this.scroller.isFinished() || this.isFling || this.moveLength == 0) {
            return;
        }
        this.cancelScroll();

        // 向下滑动
        if (this.moveLength > 0) {
            if (this.isHorizontal) {
                if (this.moveLength < this.itemWidth / 2) {
                    this.scroll(this.moveLength, 0);
                } else {
                    this.scroll(this.moveLength, this.itemWidth);
                }
            } else {
                if (this.moveLength < this.itemHeight / 2) {
                    this.scroll(this.moveLength, 0);
                } else {
                    this.scroll(this.moveLength, this.itemHeight);
                }
            }
        } else {
            if (this.isHorizontal) {
                if (-this.moveLength < this.itemWidth / 2) {
                    this.scroll(this.moveLength, 0);
                } else {
                    this.scroll(this.moveLength, -this.itemWidth);
                }
            } else {
                if (-this.moveLength < this.itemHeight / 2) {
                    this.scroll(this.moveLength, 0);
                } else {
                    this.scroll(this.moveLength, -this.itemHeight);
                }
            }
        }
        return;
    }

    // 平滑滚动
    private void scroll(float from, int to) {
        if (this.isHorizontal) {
            this.lastScrollX = (int) from;
            this.isMovingCenter = true;
            this.scroller.startScroll((int) from, 0, 0, 0);
            this.scroller.setFinalX(to);
        } else {
            this.lastScrollY = (int) from;
            this.isMovingCenter = true;
            this.scroller.startScroll(0, (int) from, 0, 0);
            this.scroller.setFinalY(to);
        }
        this.invalidate();
        return;
    }

    // 惯性滑动,
    private void fling(float from, float vel) {
        if (this.isHorizontal) {
            this.lastScrollX = (int) from;
            this.isFling = true;
            // 最多可以惯性滑动10个item
            this.scroller.fling((int) from, 0, (int) vel, 0, -10 * this.itemWidth, 10 * this.itemWidth, 0, 0);
        } else {
            this.lastScrollY = (int) from;
            this.isFling = true;
            // 最多可以惯性滑动10个item
            this.scroller.fling(0, (int) from, 0, (int) vel, 0, 0, -10 * this.itemHeight, 10 * this.itemHeight);
        }
        this.invalidate();
        return;
    }

    private void notifySelected() {
        if (this.listener != null) {
            // 告诉监听器选择完毕
            this.postDelayed(new Runnable() {
                @Override
                public void run() {
                    ScrollPickerView.this.listener.onSelected(ScrollPickerView.this, ScrollPickerView.this.selected);
                    return;
                }
            }, 10);
        }
        return;
    }

    /**
     * 自动滚动(必须设置为可循环滚动)
     *
     * @param position
     * @param duration
     * @param speed    每毫秒移动的像素点
     */
    public void autoScrollFast(final int position, long duration, float speed, final Interpolator interpolator) {
        if (this.isAutoScrolling || !this.isCirculation) {
            return;
        }
        this.cancelScroll();
        this.isAutoScrolling = true;

        int length = (int) (speed * duration);
        int circle = (int) (length * 1f / (this.data.size() * this.itemSize) + 0.5f); // 圈数
        circle = circle <= 0 ? 1 : circle;

        int aPlan = circle * (this.data.size()) * this.itemSize + (this.selected - position) * this.itemSize;
        int bPlan = aPlan + (this.data.size()) * this.itemSize; // 多一圈
        // 让其尽量接近length
        final int end = Math.abs(length - aPlan) < Math.abs(length - bPlan) ? aPlan : bPlan;

        this.autoScrollAnimator.cancel();
        this.autoScrollAnimator.setIntValues(0, end);
        this.autoScrollAnimator.setInterpolator(interpolator);
        this.autoScrollAnimator.setDuration(duration);
        this.autoScrollAnimator.removeAllUpdateListeners();
        if (end != 0) {
            // itemHeight为0 导致endy=0
            this.autoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float rate = 0;
                    rate = animation.getCurrentPlayTime() * 1f / animation.getDuration();
                    ScrollPickerView.this.computeScroll((int) animation.getAnimatedValue(), end, rate);
                    return;
                }
            });
            this.autoScrollAnimator.removeAllListeners();
            this.autoScrollAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    ScrollPickerView.this.autoScrollStop();
                    return;
                }
            });
            this.autoScrollAnimator.start();
        } else {
            this.computeScroll(end, end, 1);
            this.autoScrollStop();
        }
        return;
    }

    /**
     * 设置停止标记
     */
    private void autoScrollStop() {
        this.selected = this.defaultValue;
        this.isAutoScrolling = false;
        return;
    }

    /**
     * 自动滚动,默认速度为 0.6dp/ms
     *
     * @see ScrollPickerView#autoScrollFast(int, long, float, Interpolator)
     */
    public void autoScrollFast(final int position, long duration) {
        float speed = dip2px(0.6f);
        this.autoScrollFast(position, duration, speed, this.autoScrollInterpolator);
        return;
    }

    /**
     * 自动滚动
     *
     * @see ScrollPickerView#autoScrollFast(int, long, float, Interpolator)
     */
    public void autoScrollFast(final int position, long duration, float speed) {
        this.autoScrollFast(position, duration, speed, this.autoScrollInterpolator);
        return;
    }

    /**
     * 滚动到指定位置
     *
     * @param toPosition    需要滚动到的位置
     * @param duration      滚动时间
     * @param interpolator
     */
    public void autoScrollToPosition(int toPosition, long duration, final Interpolator interpolator) {
        toPosition = toPosition % this.data.size();
        final int endY = (this.selected - toPosition) * this.itemHeight;
        this.autoScrollTo(endY, duration, interpolator, false);
        return;
    }

    /**
     * @param endY          需要滚动到的位置
     * @param duration      滚动时间
     * @param interpolator
     * @param canIntercept 能否终止滚动,比如触摸屏幕终止滚动
     */
    public void autoScrollTo(final int endY, long duration, final Interpolator interpolator, boolean canIntercept) {
        if (this.isAutoScrolling) {
            return;
        }
        final boolean temp = this.disallowTouch;
        this.disallowTouch = !canIntercept;
        this.isAutoScrolling = true;
        this.autoScrollAnimator.cancel();
        this.autoScrollAnimator.setIntValues(0, endY);
        this.autoScrollAnimator.setInterpolator(interpolator);
        this.autoScrollAnimator.setDuration(duration);
        this.autoScrollAnimator.removeAllUpdateListeners();
        this.autoScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float rate = 0;
                rate = animation.getCurrentPlayTime() * 1f / animation.getDuration();
                ScrollPickerView.this.computeScroll((int) animation.getAnimatedValue(), endY, rate);
                return;
            }
        });
        this.autoScrollAnimator.removeAllListeners();
        this.autoScrollAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                ScrollPickerView.this.autoScrollStop();
                ScrollPickerView.this.disallowTouch = temp;
                return;
            }
        });
        this.autoScrollAnimator.start();
        return;
    }


    /**
     * 停止自动滚动
     */
    public void stopAutoScroll() {
        this.autoScrollStop();
        this.autoScrollAnimator.cancel();
        return;
    }

    private static class SlotInterpolator implements Interpolator {
        @Override
        public float getInterpolation(float input) {
            return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
        }
    }

    /**
     * 快速滑动时,惯性滑动一段距离
     *
     * @author huangziwei
     */
    private class FlingOnGestureListener extends SimpleOnGestureListener {

        private boolean mIsScrollingLastTime = false;

        public boolean onDown(MotionEvent e) {
            if (ScrollPickerView.this.disallowInterceptTouch) {  // 不允许父组件拦截事件
                ViewParent parent = getParent();
                if (parent != null) {
                    parent.requestDisallowInterceptTouchEvent(true);
                }
            }
            this.mIsScrollingLastTime = isScrolling(); // 记录是否从滚动状态终止
            // 点击时取消所有滚动效果
            ScrollPickerView.this.cancelScroll();
            ScrollPickerView.this.lastMoveY = e.getY();
            ScrollPickerView.this.lastMoveX = e.getX();
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, final float velocityY) {
            // 惯性滑动
            if (ScrollPickerView.this.isInertiaScroll) {
                cancelScroll();
                if (ScrollPickerView.this.isHorizontal) {
                    ScrollPickerView.this.fling(ScrollPickerView.this.moveLength, velocityX);
                } else {
                    ScrollPickerView.this.fling(ScrollPickerView.this.moveLength, velocityY);
                }
            }
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            ScrollPickerView.this.lastMoveY = e.getY();
            ScrollPickerView.this.lastMoveX = e.getX();
            float lastMove = 0;
            if (isHorizontal()) {
                ScrollPickerView.this.centerPoint = ScrollPickerView.this.centerX;
                lastMove = ScrollPickerView.this.lastMoveX;
            } else {
                ScrollPickerView.this.centerPoint = ScrollPickerView.this.centerY;
                lastMove = ScrollPickerView.this.lastMoveY;
            }
            if (ScrollPickerView.this.canTap && !ScrollPickerView.this.isScrolling() && !this.mIsScrollingLastTime) {
                if (lastMove >= ScrollPickerView.this.centerPoint && lastMove <= ScrollPickerView.this.centerPoint + ScrollPickerView.this.itemSize) {
                    ScrollPickerView.this.performClick();
                } else if (lastMove < ScrollPickerView.this.centerPoint) {
                    int move = itemSize;
                    ScrollPickerView.this.autoScrollTo(move, 150, ScrollPickerView.this.autoScrollInterpolator, false);
                } else if (lastMove > ScrollPickerView.this.centerPoint + ScrollPickerView.this.itemSize) {
                    int move = -ScrollPickerView.this.itemSize;
                    ScrollPickerView.this.autoScrollTo(move, 150, ScrollPickerView.this.autoScrollInterpolator, false);
                } else {
                    ScrollPickerView.this.moveToCenter();
                }
            } else {
                ScrollPickerView.this.moveToCenter();
            }
            return true;
        }
    }

    public List<T> getData() {
        return this.data;
    }

    public void setData(List<T> data) {
        if (data == null) {
            this.data = new ArrayList<T>();
        } else {
            this.data = data;
        }
        this.selected = this.data.size() / 2;
        this.invalidate();
        return;
    }

    public T getSelectedItem() {
        return this.data.get(this.selected);
    }

    public int getSelectedPosition() {
        return this.selected;
    }

    public void setSelectedPosition(int position) {
        if (position < 0 || position > this.data.size() - 1 || position == this.selected) {
            return;
        }
        this.selected = position;
        this.invalidate();
        if (this.listener != null) {
            this.notifySelected();
        }
        return;
    }

    public void setOnSelectedListener(OnSelectedListener listener) {
        this.listener = listener;
        return;
    }

    public OnSelectedListener getListener() {
        return this.listener;
    }

    public boolean isInertiaScroll() {
        return this.isInertiaScroll;
    }

    public void setInertiaScroll(boolean inertiaScroll) {
        this.isInertiaScroll = inertiaScroll;
        return;
    }

    public boolean isIsCirculation() {
        return this.isCirculation;
    }

    public void setIsCirculation(boolean isCirculation) {
        this.isCirculation = isCirculation;
        return;
    }

    public boolean isDisallowInterceptTouch() {
        return this.disallowInterceptTouch;
    }

    public int getVisibleItemCount() {
        return this.visibleItemCount;
    }

    public void setVisibleItemCount(int visibleItemCount) {
        this.visibleItemCount = visibleItemCount;
        this.reset();
        this.invalidate();
        return;
    }

    /**
     * 是否允许父元素拦截事件,设置true后可以保证在ScrollView下正常滚动
     */
    public void setDisallowInterceptTouch(boolean disallowInterceptTouch) {
        this.disallowInterceptTouch = disallowInterceptTouch;
        return;
    }

    public int getItemHeight() {
        return this.itemHeight;
    }

    public int getItemWidth() {
        return this.itemWidth;
    }

    /**
     * @return 当垂直滚动时,itemSize = itemHeight;水平滚动时,itemSize = itemWidth
     */
    public int getItemSize() {
        return this.itemSize;
    }

    /**
     * @return 中间item的起始坐标x(不考虑偏移), 当垂直滚动时,x = centerPosition*itemWidth
     */
    public int getCenterX() {
        return this.centerX;
    }

    /**
     * @return 中间item的起始坐标y(不考虑偏移), 当垂直滚动时,y= centerPosition*itemHeight
     */
    public int getCenterY() {
        return this.centerY;
    }

    /**
     * @return 当垂直滚动时,centerPoint = centerY;水平滚动时,centerPoint = centerX
     */
    public int getCenterPoint() {
        return this.centerPoint;
    }

    public boolean isDisallowTouch() {
        return this.disallowTouch;
    }

    /**
     * 设置是否允许手动触摸滚动
     *
     * @param disallowTouch
     */
    public void setDisallowTouch(boolean disallowTouch) {
        this.disallowTouch = disallowTouch;
        return;
    }

    /**
     * 中间item的位置,0 <= centerPosition <= visibleItemCount
     *
     * @param centerPosition
     */
    public void setCenterPosition(int centerPosition) {
        if (centerPosition < 0) {
            this.centerPosition = 0;
        } else if (centerPosition >= this.visibleItemCount) {
            this.centerPosition = this.visibleItemCount - 1;
        } else {
            this.centerPosition = centerPosition;
        }
        this.centerY = this.centerPosition * this.itemHeight;
        this.invalidate();
        return;
    }

    /**
     * 中间item的位置,默认为 visibleItemCount / 2
     *
     * @return
     */
    public int getCenterPosition() {
        return this.centerPosition;
    }

    public void setCenterItemBackground(Drawable centerItemBackground) {
        this.centerItemBackground = centerItemBackground;
        this.centerItemBackground.setBounds(this.centerX, this.centerY, this.centerX + this.itemWidth, this.centerY + this.itemHeight);
        this.invalidate();
        return;
    }

    public void setCenterItemBackground(int centerItemBackgroundColor) {
        this.centerItemBackground = new ColorDrawable(centerItemBackgroundColor);
        this.centerItemBackground.setBounds(this.centerX, this.centerY, this.centerX + this.itemWidth, this.centerY + this.itemHeight);
        this.invalidate();
        return;
    }

    public Drawable getCenterItemBackground() {
        return this.centerItemBackground;
    }

    public boolean isScrolling() {
        return this.isFling || this.isMovingCenter || this.isAutoScrolling;
    }

    public boolean isFling() {
        return this.isFling;
    }

    public boolean isMovingCenter() {
        return this.isMovingCenter;
    }

    public boolean isAutoScrolling() {
        return this.isAutoScrolling;
    }

    public boolean isCanTap() {
        return this.canTap;
    }

    /**
     * 设置 单击切换选项或触发点击监听器
     *
     * @param canTap
     */
    public void setCanTap(boolean canTap) {
        this.canTap = canTap;
        return;
    }

    public boolean isHorizontal() {
        return this.isHorizontal;
    }

    public boolean isVertical() {
        return !this.isHorizontal;
    }

    public void setHorizontal(boolean horizontal) {
        if (this.isHorizontal == horizontal) {
            return;
        }
        this.isHorizontal = horizontal;
        this.reset();
        if (this.isHorizontal) {
            this.itemSize = this.itemWidth;
        } else {
            this.itemSize = this.itemHeight;
        }
        this.invalidate();
        return;
    }

    public void setVertical(boolean vertical) {
        if (this.isHorizontal == !vertical) {
            return;
        }
        this.isHorizontal = !vertical;
        this.reset();
        if (this.isHorizontal) {
            this.itemSize = this.itemWidth;
        } else {
            this.itemSize = this.itemHeight;
        }
        this.invalidate();
        return;
    }

    public boolean isDrawAllItem() {
        return this.drawAllItem;
    }

    public void setDrawAllItem(boolean drawAllItem) {
        this.drawAllItem = drawAllItem;
        return;
    }

    /**
     * @author huangziwei
     */
    public interface OnSelectedListener {
        void onSelected(ScrollPickerView scrollPickerView, int position);
    }

    public int dip2px(float dipVlue) {
        DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
        float sDensity = metrics.density;
        return (int) (dipVlue * sDensity + 0.5F);
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (visibility == VISIBLE) {
            this.moveToCenter();
        }
        return;
    }

    public void setDefaultValue(int defaultValue) {
        this.defaultValue = defaultValue;
        return;
    }
}

BitmapScrollPicker.class

/**
 * 图片滚动选择器
 */
public class BitmapScrollPicker extends ScrollPickerView<Bitmap> {

    // 图片绘制模式:填充
    public final static int DRAW_MODE_FULL = 1;
    // 图片绘制模式:居中
    public final static int DRAW_MODE_CENTER = 2;
    // 图片绘制模式:指定大小
    public final static int DRAW_MODE_SPECIFIED_SIZE = 3;

    private int measureWidth;
    private int measureHeight;
    private Rect rect1;
    private Rect rect2;
    private Rect specifiedSizeRect;
    private Rect rectTemp;
    private int drawMode = BitmapScrollPicker.DRAW_MODE_CENTER;

    // item内容缩放倍数
    private float minScale = 1;
    private float maxScale = 1;

    private int specifiedSizeWidth = -1;
    private int specifiedSizeHeight = -1;

    public BitmapScrollPicker(Context context) {
        super(context);
        this.initView(context);
        return;
    }

    public BitmapScrollPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initView(context);
        return;
    }

    public BitmapScrollPicker(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.initView(context);
        return;
    }

    public BitmapScrollPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.initView(context);
        return;
    }

    /**
     * 初始化视图
     *
     * @param context
     */
    private void initView(Context context) {
        this.rect1 = new Rect();
        this.rect2 = new Rect();
        this.specifiedSizeRect = new Rect();
        this.rectTemp = new Rect();
        return;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        this.measureWidth = this.getMeasuredWidth();
        this.measureHeight = this.getMeasuredHeight();
        // 当view的的大小确定后,选择器中item的某些位置也可确定。当水平滚动时,item的顶部和底部的坐标y可确定;当垂直滚动时,item的左边和右边的坐标x可确定
        if (this.drawMode == BitmapScrollPicker.DRAW_MODE_FULL) { // 填充
            if (isHorizontal()) {
                this.rect2.top = 0;
                this.rect2.bottom = this.measureHeight;
            } else {
                this.rect2.left = 0;
                this.rect2.right = this.measureWidth;
            }
        } else if (this.drawMode == BitmapScrollPicker.DRAW_MODE_SPECIFIED_SIZE) { // 指定大小
            if (this.specifiedSizeWidth == -1) {
                this.specifiedSizeWidth = this.measureWidth;
                this.specifiedSizeHeight = this.measureHeight;
            }
            this.setDrawModeSpecifiedSize(this.specifiedSizeWidth, this.specifiedSizeHeight);
        } else { // 居中
            int size;
            if (this.isHorizontal()) {
                size = Math.min(this.measureHeight, this.getItemWidth());
            } else {
                size = Math.min(this.measureWidth, this.getItemHeight());
            }
            if (this.isHorizontal()) {
                this.rect2.top = this.measureHeight / 2 - size / 2;
                this.rect2.bottom = this.measureHeight / 2 + size / 2;
            } else {
                this.rect2.left = this.measureWidth / 2 - size / 2;
                this.rect2.right = this.measureWidth / 2 + size / 2;
            }
        }
        return;
    }

    @Override
    public void drawItem(Canvas canvas, List<Bitmap> data, int position, int relative, float moveLength, float top) {
        int itemSize = this.getItemSize();

        Bitmap bitmap = data.get(position);

        this.rect1.right = bitmap.getWidth();
        this.rect1.bottom = bitmap.getHeight();

        int span = 0;

        // 根据不同的绘制模式,计算出item内容的最终绘制位置和大小
        // 当水平滚动时,计算item的左边和右边的坐标x;当垂直滚动时,item的顶部和底部的坐标y
        if (this.drawMode == BitmapScrollPicker.DRAW_MODE_FULL) { // 填充
            span = 0;
            if (this.isHorizontal()) {
                this.rect2.left = (int) top + span;
                this.rect2.right = (int) (top + itemSize - span);
            } else {
                this.rect2.top = (int) top + span;
                this.rect2.bottom = (int) (top + itemSize - span);
            }
            this.rectTemp.set(this.rect2);
            this.scale(this.rectTemp, relative, itemSize, moveLength);
            canvas.drawBitmap(bitmap, this.rect1, this.rectTemp, null);
        } else if (this.drawMode == BitmapScrollPicker.DRAW_MODE_SPECIFIED_SIZE) { // 指定大小
            if (this.isHorizontal()) {
                span = (itemSize - this.specifiedSizeWidth) / 2;

                this.specifiedSizeRect.left = (int) top + span;
                this.specifiedSizeRect.right = (int) top + span + this.specifiedSizeWidth;
            } else {
                span = (itemSize - this.specifiedSizeHeight) / 2;

                this.specifiedSizeRect.top = (int) top + span;
                this.specifiedSizeRect.bottom = (int) top + span + this.specifiedSizeHeight;
            }
            this.rectTemp.set(this.specifiedSizeRect);
            this.scale(this.rectTemp, relative, itemSize, moveLength);
            canvas.drawBitmap(bitmap, this.rect1, this.rectTemp, null);
        } else { // 居中
            if (this.isHorizontal()) {
                float scale = this.rect2.height() * 1f / bitmap.getHeight();
                span = (int) ((itemSize - bitmap.getWidth() * scale) / 2);
            } else {
                float scale = this.rect2.width() * 1f / bitmap.getWidth();
                span = (int) ((itemSize - bitmap.getHeight() * scale) / 2);
            }
            if (this.isHorizontal()) {
                this.rect2.left = (int) (top + span);
                this.rect2.right = (int) (top + itemSize - span);
            } else {
                this.rect2.top = (int) (top + span);
                this.rect2.bottom = (int) (top + itemSize - span);
            }
            this.rectTemp.set(this.rect2);
            this.scale(this.rectTemp, relative, itemSize, moveLength);
            canvas.drawBitmap(bitmap, this.rect1, this.rectTemp, null);
        }
        return;
    }

    // 缩放item内容
    private void scale(Rect rect, int relative, int itemSize, float moveLength) {
        if (this.minScale == 1 && this.maxScale == 1) {
            return;
        }

        float spanWidth;
        float spanHeight;

        if (this.minScale == this.maxScale) {
            spanWidth = (rect.width() - this.minScale * rect.width()) / 2;
            spanHeight = (rect.height() - this.minScale * rect.height()) / 2;
            rect.left += spanWidth;
            rect.right -= spanWidth;
            rect.top += spanHeight;
            rect.bottom -= spanHeight;
            return;
        }

        if (relative == -1 || relative == 1) { // 上一个或下一个
            // 处理上一个item且向上滑动 或者 处理下一个item且向下滑动,
            if ((relative == -1 && moveLength < 0) || (relative == 1 && moveLength > 0)) {
                spanWidth = (rect.width() - this.minScale * rect.width()) / 2;
                spanHeight = (rect.height() - this.minScale * rect.height()) / 2;
            } else { // 计算渐变
                float rate = Math.abs(moveLength) / itemSize;
                spanWidth = (rect.width() - (this.minScale + (this.maxScale - this.minScale) * rate) * rect.width()) / 2;
                spanHeight = (rect.height() - (this.minScale + (this.maxScale - this.minScale) * rate) * rect.height()) / 2;
            }
        } else if (relative == 0) { // 中间item
            float rate = (itemSize - Math.abs(moveLength)) / itemSize;
            spanWidth = (rect.width() - (this.minScale + (this.maxScale - this.minScale) * rate) * rect.width()) / 2;
            spanHeight = (rect.height() - (this.minScale + (this.maxScale - this.minScale) * rate) * rect.height()) / 2;
        } else {
            spanWidth = (rect.width() - this.minScale * rect.width()) / 2;
            spanHeight = (rect.height() - this.minScale * rect.height()) / 2;
        }

        rect.left += spanWidth;
        rect.right -= spanWidth;
        rect.top += spanHeight;
        rect.bottom -= spanHeight;
        return;
    }

    /**
     * 图片绘制模式 ,默认为居中
     *
     * @param mode
     */
    public void setDrawMode(int mode) {
        int size = 0;
        if (this.isHorizontal()) {
            size = Math.min(this.measureHeight, this.getItemWidth());
        } else {
            size = Math.min(this.measureWidth, this.getItemHeight());
        }
        this.drawMode = mode;
        if (this.drawMode == BitmapScrollPicker.DRAW_MODE_FULL) {
            if (this.isHorizontal()) {
                this.rect2.top = 0;
                this.rect2.bottom = this.measureHeight;
            } else {
                this.rect2.left = 0;
                this.rect2.right = this.measureWidth;
            }
        } else if (this.drawMode == BitmapScrollPicker.DRAW_MODE_SPECIFIED_SIZE) {

        } else {
            if (this.isHorizontal()) {
                this.rect2.top = this.measureHeight / 2 - size / 2;
                this.rect2.bottom = this.measureHeight / 2 + size / 2;
            } else {
                this.rect2.left = this.measureWidth / 2 - size / 2;
                this.rect2.right = this.measureWidth / 2 + size / 2;
            }
        }
        this.invalidate();
        return;
    }

    public void setDrawModeSpecifiedSize(int width, int height) {
        if (this.isHorizontal()) {
            this.specifiedSizeRect.top = (this.measureHeight - height) / 2;
            this.specifiedSizeRect.bottom = (this.measureHeight - height) / 2 + height;
        } else {
            this.specifiedSizeRect.left = (this.measureWidth - width) / 2;
            this.specifiedSizeRect.right = (this.measureWidth - width) / 2 + width;
        }
        this.specifiedSizeWidth = width;
        this.specifiedSizeHeight = height;
        this.invalidate();
        return;
    }

}

NumberPickerView.class

/**
 * 数字滚动控件
 */
public class NumberPickerView extends LinearLayout implements ScrollPickerView.OnSelectedListener {

    private Map<Integer, BitmapScrollPicker> bitmapScrollPickerMap;
    private MediaPlayer dingMedia;
    private boolean playing;
    private OnScrollListener onScrollListener;
    private int numberLenght;
    private int scrollIndex;
    private int itemWidth;
    private int itemHeight;
    private int number;

    public interface OnScrollListener {
        void onScrolled();
    }

    public NumberPickerView(Context context) {
        super(context);
        this.initView(context, null);
        return;
    }

    public NumberPickerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initView(context, attrs);
        return;
    }

    public NumberPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.initView(context, attrs);
        return;
    }

    public NumberPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        this.initView(context, attrs);
        return;
    }

    /**
     * 初始化视图
     *
     * @param context
     * @param attrs
     */
    private void initView(Context context, AttributeSet attrs) {

        this.itemWidth = 70;
        this.itemHeight = 95;

        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.number_scroll_view);
            this.itemWidth = array.getDimensionPixelSize(R.styleable.number_scroll_view_itemWidth, itemWidth);
            this.itemHeight = array.getDimensionPixelSize(R.styleable.number_scroll_view_itemHeight, itemHeight);
            array.recycle();
        }

        this.setClickable(false);
        this.setOrientation(LinearLayout.HORIZONTAL);
        this.setBackgroundColor(Color.TRANSPARENT);
        this.playing = false;

        CopyOnWriteArrayList<Bitmap> bitmaps = new CopyOnWriteArrayList<Bitmap>();
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_0));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_1));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_2));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_3));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_4));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_5));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_6));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_7));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_8));
        bitmaps.add(BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.num_9));

        if (this.bitmapScrollPickerMap != null) {
            this.bitmapScrollPickerMap.clear();
        }

        this.bitmapScrollPickerMap = new LinkedHashMap<Integer, BitmapScrollPicker>();

        int margin = 2;
        int total = 5;  // 最大数值99999

        LayoutParams params = new LayoutParams(this.itemWidth, this.itemHeight);
        params.rightMargin = margin;
        params.leftMargin = margin;

        for (int i = 0; i < total; i++) {
            BitmapScrollPicker bitmapScrollPicker = new BitmapScrollPicker(this.getContext(), null);
            bitmapScrollPicker.setVisibleItemCount(1);
            bitmapScrollPicker.setDrawMode(BitmapScrollPicker.DRAW_MODE_SPECIFIED_SIZE);
            bitmapScrollPicker.setData(bitmaps);
            bitmapScrollPicker.setSelectedPosition(0);
            bitmapScrollPicker.setDisallowTouch(true);
            bitmapScrollPicker.setDrawModeSpecifiedSize(params.width, params.height);
            this.bitmapScrollPickerMap.put(i, bitmapScrollPicker);
            this.addView(bitmapScrollPicker, params);
        }

        return;
    }

    @Override
    public void onSelected(ScrollPickerView scrollPickerView, int position) {
        if (scrollPickerView != null && this.playing) {

            this.scrollIndex++;

            if (this.dingMedia != null) {
                // this.dingMedia.start();
            }

            if (this.scrollIndex == this.numberLenght) {
                if (this.onScrollListener != null) {
                    this.onScrollListener.onScrolled();
                }
                this.playing = false;
            }

        }
        return;
    }

    /**
     * 设置数字
     *
     * @param number
     */
    public void setNumber(int number) {
        this.number = number;
        int length = String.valueOf(number).length();
        if (length > 4) {
            double f = 1;
//            if (length == 6) {
//                f = 0.75;
//            } else if (length == 7) {
//                f = 0.65;
//            }
            for (Map.Entry<Integer, BitmapScrollPicker> entry : this.bitmapScrollPickerMap.entrySet()) {
                BitmapScrollPicker bitmapScrollPicker = entry.getValue();
                LayoutParams params = (LayoutParams) bitmapScrollPicker.getLayoutParams();
                params.width = (int) (this.itemWidth * f);
                params.height = (int) (this.itemHeight * f);
                bitmapScrollPicker.setLayoutParams(params);
                bitmapScrollPicker.setDrawModeSpecifiedSize(params.width, params.height);
                bitmapScrollPicker.setVisibility(GONE);
            }
        }

        int total = this.bitmapScrollPickerMap.size();
        int start = total - length;

        for (Map.Entry<Integer, BitmapScrollPicker> entry : this.bitmapScrollPickerMap.entrySet()) {

            BitmapScrollPicker bitmapScrollPicker = entry.getValue();

            int key = entry.getKey();
            if (key >= start) {
                bitmapScrollPicker.setOnSelectedListener(this);
                bitmapScrollPicker.setVisibility(View.VISIBLE);
            } else {
                bitmapScrollPicker.setOnSelectedListener(null);
                bitmapScrollPicker.setVisibility(GONE);
            }

        }
        return;
    }

    /**
     * 开始滚动数字
     *
     * @param onScrollListener
     */
    public void startNumber(OnScrollListener onScrollListener) {

        if (this.number < 0 || this.number > 9999999) {
            if (this.onScrollListener != null) {
                this.onScrollListener.onScrolled();
            }
            return;
        }

        if (this.playing) {
            return;
        }

        this.playing = true;

        char[] numbers = String.valueOf(number).toCharArray();

        this.numberLenght = numbers.length;
        this.onScrollListener = onScrollListener;
        this.scrollIndex = 0;

        int duration = 2000;
        int add = 300;

        int total = this.bitmapScrollPickerMap.size();
        int start = total - numbers.length;

        for (Map.Entry<Integer, BitmapScrollPicker> entry : this.bitmapScrollPickerMap.entrySet()) {

            BitmapScrollPicker bitmapScrollPicker = entry.getValue();

            int key = entry.getKey();
            if (key >= start) {
                int num = Integer.parseInt(String.valueOf(numbers[key - start]));
                bitmapScrollPicker.setDefaultValue(num);
                bitmapScrollPicker.autoScrollFast(num, duration + (total - key - start + 1) * add);
            }

        }

        return;
    }

    public void stop() {
        if (this.dingMedia != null) {
            this.dingMedia.stop();
        }
        return;
    }

activity_main.xml

<?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"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/white"
                android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:orientation="vertical">

        <com.example.demo.control.NumberPickerView
            android:id="@+id/number_picker_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:itemHeight="161dp"
            app:itemWidth="119dp"/>

        <Button
            android:id="@+id/button"
            android:layout_width="150dp"
            android:layout_height="50dp"
            android:layout_marginTop="50dp"
            android:background="@drawable/button"
            android:stateListAnimator="@null"
            android:text="开启滚动"
            android:textColor="#FFFFFFFF"
            android:textSize="20dp"/>

    </LinearLayout>

</RelativeLayout>

button.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#FFB9860C"/>
            <gradient android:angle="135"
                      android:centerColor="#FFD5A01A"
                      android:centerX="0.5"
                      android:centerY="0.5"
                      android:endColor="#FFE7BD2F"
                      android:startColor="#FFB9860C"/>
            <corners android:radius="100dp"/>
        </shape>
    </item>
    <item android:state_enabled="false">
        <shape android:shape="rectangle">
            <solid android:color="#FFCCCCCC"/>
            <corners android:radius="100dp"/>
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid android:color="#FFBC8B15"/>
            <gradient android:angle="135"
                      android:centerColor="#FFD7A62A"
                      android:centerX="0.5"
                      android:centerY="0.5"
                      android:endColor="#FFF3D46E"
                      android:startColor="#FFBC8B15"/>
            <corners android:radius="100dp"/>
        </shape>
    </item>
</selector>

Demo下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值