android 自定义View之SubmitView

转载请注明出处:王亟亟的大牛之路

最近看了一大堆的自定义View多数都可以充当耗时操作的交互界面,再接再厉再传一个SubmitView,一个和可用于模仿提交等待与用户交互用的一个自定义View

效果图:

项目结构:

一个Android Studio项目。也可以转成Eclipse,因为没有涉及到打包啊lib的一些操作,连资源文件都没用,所以可以直接Copy过去。

自定义View

public class SubmitView extends View {

    private static final int COLOR_BACK = 0xff00cd97;
    private static final int COLOR_GREY = 0xffb9b9b9;

    private int mColor = COLOR_BACK;

    //
    private Paint mBorderPaint;
    private Paint mTextPaint;
    private Paint mBackPaint;

    private int mWidth;
    private int mHeight;
    private int mRadius;
    private static final int PADDING = 5;

    private Rect mTextBounds;

    private boolean mShowText = true;
    private static final String TEXT_SUBMIT = "提交";
    private String mText = TEXT_SUBMIT;
    private int mStrokeWidth;

    // indicate if view can click
    private boolean mCanClick = true;

    // indicate the animator state
    private AniState mAniState = AniState.INIT;

    private boolean mIfReset = false;

    enum AniState {
        INIT,
        FIRST_START, // start animation which change backcolor and text color
        FIRST_STOP,
        SECOND_START,  // second animation which change "submit" size for a while
        SECOND_STOP,
        THIRD_START,  // third animation which convert to circle and change back color
        THIRD_STOP,
        FOURTH_START,  // fourth animation which show the progress
        FOURTH_STOP,
        FIFTH_START,   // fifth animation which can narrow the back and border and show "correct" sign
        FIFTH_STOP;

        public boolean isPlaying() {
            return this==FIRST_START
                    || this==SECOND_START
                    || this == THIRD_START
                    || this == FOURTH_START
                    || this == FIFTH_START;
        }

        @Override
        public String toString() {
            switch (this) {
                case INIT:
                    return "***init***";
                case FIRST_START:
                    return "***first start***";
                case FIRST_STOP:
                    return "***first stop***";
                case SECOND_START:
                    return "***second start***";
                case SECOND_STOP:
                    return "***second stop***";
                case THIRD_START:
                    return "***third start***";
                case THIRD_STOP:
                    return "****third stop**";
                case FOURTH_START:
                    return "***fourth start***";
                case FOURTH_STOP:
                    return "***fourth stop***";
                case FIFTH_START:
                    return "***fifth start***";
                case FIFTH_STOP:
                    return "***fifth stop";
                default:
                    return "unknown state";
            }
        }
    }

    public SubmitView(Context context) {
        this(context, null, 0);
    }

    public SubmitView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SubmitView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs, defStyleAttr);
    }

    private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
        mBorderPaint = new Paint();
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeWidth(5);
        mBorderPaint.setAntiAlias(true);

        mBackPaint = new Paint();
        mBackPaint.setAntiAlias(true);
        mBackPaint.setStyle(Paint.Style.FILL);

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);

        mTextBounds = new Rect();
        mCanClick = true;

        mProgress = 0;
        mTargetProgress = 0;

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCanClick) {
                    mCanClick = false;
                    if (mAniState == AniState.INIT) {
                        mAniState = AniState.FIRST_START;
                        firstAniStart();
                        invalidate();
                    }
                }
            }
        });

    }

    private void firstAniStart() {
        mFirstStartT = System.currentTimeMillis();
        mFirstStopT = mFirstStartT + FIRST_DURATION;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();
            int width = mWidth - (PADDING * 2);
            int height = mHeight - (PADDING * 2);
            mRadius = width/3 > height ? height/2 : width/6;
            mStrokeWidth = mRadius/12;
            mBorderPaint.setStrokeWidth(mStrokeWidth);
            // Log.i("SubmitView", "width=" + mWidth + ", height=" + mHeight + ", radius=" + mRadius + ", strokeWidth=" + mStrokeWidth);
        }
    }

    private RectF rectF = new RectF();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i("submit", "cur state: " + mAniState); // open for debug

        switch (mAniState) {
            case INIT:
                mBorderPaint.setColor(mColor);
                mBackPaint.setColor(mColor);
                mTextPaint.setColor(mColor);
                canvas.drawRoundRect(new RectF(PADDING, mHeight / 2 - mRadius, mWidth - PADDING, mHeight / 2 + mRadius), mRadius, mRadius, mBorderPaint);
                mTextPaint.setTextSize(mRadius/2);
                mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
                canvas.drawText(mText, mWidth / 2 - mTextBounds.width() / 2, mHeight / 2 + mTextBounds.height() / 2, mTextPaint);
                break;
            case FIRST_START:
                canvas.drawRoundRect(new RectF(PADDING, mHeight / 2 - mRadius, mWidth - PADDING, mHeight / 2 + mRadius), mRadius, mRadius, mBorderPaint);
                mBackPaint.setColor(getFirstColor());
                canvas.drawRoundRect(new RectF(PADDING, mHeight / 2 - mRadius, mWidth - PADDING, mHeight / 2 + mRadius),
                        mRadius, mRadius, mBackPaint);
                mTextPaint.setColor(getFirstTextColor());
                canvas.drawText(mText, mWidth/2 - mTextBounds.width()/2, mHeight/2 + mTextBounds.height()/2, mTextPaint);
                break;
            case SECOND_START:
                canvas.drawRoundRect(new RectF(PADDING, mHeight / 2 - mRadius, mWidth - PADDING, mHeight / 2 + mRadius), mRadius, mRadius, mBorderPaint);
                canvas.drawRoundRect(new RectF(PADDING, mHeight / 2 - mRadius, mWidth - PADDING, mHeight / 2 + mRadius),
                        mRadius, mRadius, mBackPaint);
                mTextPaint.setTextSize(getSecondTextSize());
                mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
                canvas.drawText(mText, mWidth/2 - mTextBounds.width()/2, mHeight/2 + mTextBounds.height()/2, mTextPaint);
                break;
            case THIRD_START:
                int leftRect = mWidth/2 - getHorizonRadius();
                int rightRect = mWidth/2 + getHorizonRadius();
                mBackPaint.setColor(getThirdColor());
                canvas.drawRoundRect(new RectF(leftRect, mHeight / 2 - mRadius, rightRect, mHeight / 2 + mRadius),
                        mRadius, mRadius, mBackPaint);
                mBorderPaint.setColor(getThirdBorderColor());
                canvas.drawRoundRect(new RectF(leftRect, mHeight / 2 - mRadius, rightRect, mHeight / 2 + mRadius), mRadius, mRadius, mBorderPaint);
                break;
            case FOURTH_START:
                // Log.i("submit", "" + progress);
                if (mProgress>=1) {
                    if (mOnProgressDone!=null) {
                        mOnProgressDone.progressDone();
                    }
                    mAniState = AniState.FOURTH_STOP;
                    mAniState = AniState.FIFTH_START;
                    initFifthAni();
                }
                mBorderPaint.setColor(COLOR_GREY);
                canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius, mBorderPaint);
                mBorderPaint.setColor(mColor);
                canvas.drawArc(new RectF(mWidth / 2 - mRadius, mHeight / 2 - mRadius, mWidth / 2 + mRadius, mHeight/2 + mRadius),
                        START_DEGREE, 360 * mProgress, false, mBorderPaint);
                if (mProgress < mTargetProgress) {
                    mProgress += 0.005;
                    mProgress = Math.min(mProgress, 1);
                }
                break;
            case FIFTH_START:
                int leftFifthR = mWidth/2 - getFifthHorizonR();
                int rightFifthR = mWidth/2 + getFifthHorizonR();
                canvas.drawRoundRect(new RectF(leftFifthR, mHeight/2-mRadius, rightFifthR, mHeight/2 + mRadius),
                        mRadius, mRadius, mBorderPaint);
                mBackPaint.setColor(getFifthColor());
                canvas.drawRoundRect(new RectF(leftFifthR, mHeight / 2 - mRadius, rightFifthR, mHeight / 2 + mRadius),
                        mRadius, mRadius, mBackPaint);
                drawCorrectSign(canvas, getFifthRatio());
                if (getFifthRatio()>=1 && mIfReset) {
                    Log.i("submit", "reset is valid");
                    mIfReset = false;
                    mAniState = AniState.INIT;
                    initView(null, null, 0);
                    invalidate();
                }
                break;
            case FIFTH_STOP:
                canvas.drawRoundRect(new RectF(PADDING, mHeight/2 - mRadius, mWidth - PADDING, mHeight/2 + mRadius),
                    mRadius, mRadius, mBorderPaint);
                canvas.drawRoundRect(new RectF(PADDING, mHeight/2 - mRadius, mWidth - PADDING, mHeight/2 + mRadius),
                        mRadius, mRadius, mBackPaint);
                drawCorrectSign(canvas, 1);
                break;
        }

        if (mAniState.isPlaying()) {
            invalidate();
        }

    }

    // private int forDeubug = 0; // to count for debug

    // first animation
    private static final int FIRST_DURATION = 300;
    private long mFirstStartT;
    private long mFirstStopT;

    private float getFirstRatio() {
        long now = System.currentTimeMillis();
        if (now >= mFirstStopT) {
            mAniState = AniState.FIRST_STOP;
            mAniState = AniState.SECOND_START;
            initSecAni();
            return 1;
        }
        float ratio = (float)(now - mFirstStartT) / (float) FIRST_DURATION;

        return ratio > 1 ? 1 : ratio;

    }

    private int getFirstColor() {

        return Color.argb(getFirstRatio()==1 ? 0xff : (int)(getFirstRatio() * 0xff), Color.red(mColor), Color.green(mColor), Color.blue(mColor));
    }

    private int getFirstTextColor() {
        float ratio = getFirstRatio();
        if (ratio==1) {
            return 0xffffffff;
        }
        int startRed = Color.red(mColor);
        int red = startRed + (int) ((0xff - startRed) * ratio);

        int startGreen = Color.green(mColor);
        int green = startGreen + (int) ((0xff - startGreen) * ratio);

        int startBlue = Color.blue(mColor);
        int blue = startBlue + (int) ((0xff - startBlue) * ratio);

        return Color.argb(0xff, red, green, blue);
    }

    // second animation which resize the "submit" size
    private static final long SECOND_DURATION = 300;
    private long mSecStartT;
    private long mSecStopT;

    private void initSecAni() {
        mSecStartT = System.currentTimeMillis();
        mSecStopT = mSecStartT + SECOND_DURATION;
    }

    private float getSecondTextSize() {
        long now = System.currentTimeMillis();
        if (now >= mSecStopT) {
            mAniState = AniState.SECOND_STOP;
            mAniState = AniState.THIRD_START;
            initThirdAni();
            return mRadius/2;
        }

        float ratio = (now - (mSecStartT + SECOND_DURATION/2))/(float)(SECOND_DURATION/2);
        return mRadius*3/8 + mRadius/8 * Math.abs(ratio);
    }

    // third animation which change back color and change to a circle
    private long mThirdStartT;
    private long mThirdStopT;
    private long THIRD_DURATION = 400;

    private void initThirdAni() {
        mThirdStartT = System.currentTimeMillis();
        mThirdStopT = mThirdStartT + THIRD_DURATION;
    }

    private float getThirdRatio() {
        long now = System.currentTimeMillis();
        if (now >= mThirdStopT) {
            mAniState = AniState.THIRD_STOP;
            mAniState = AniState.FOURTH_START;
            initFourthAni();
            return 1;
        }

        float ratio = (now - mThirdStartT)/(float)THIRD_DURATION;
        return ratio >= 1 ? 1 : ratio;
    }

    private int getHorizonRadius() {
        float ratio = getThirdRatio();
        int horizonRadius = mRadius + (int) ((1-ratio) * (mWidth/2 - PADDING - mRadius));
        return horizonRadius;
    }

    private int getThirdColor() {
        float ratio = getThirdRatio();
        int alpha = (int) ((1-ratio) * 0xff);
        return Color.argb(alpha, Color.red(mColor), Color.green(mColor), Color.blue(mColor));
    }

    private int getThirdBorderColor() {
        float ratio = getThirdRatio();
        int redStart = Color.red(mColor);
        int greenStart = Color.green(mColor);
        int blueStart = Color.blue(mColor);

        int curRed = redStart + (int) ((Color.red(COLOR_GREY) - redStart) * ratio);
        int curGreen = greenStart + (int) ((Color.green(COLOR_GREY) - greenStart) * ratio);
        int curBlue = blueStart + (int) ((Color.blue(COLOR_GREY) - blueStart) * ratio);

        return Color.argb(0xff, curRed, curGreen, curBlue);
    }

    // used for fourth animation but this not use time to calculate animation
    private static final int START_DEGREE = 270;
    private float mProgress = 0f;
    private float mTargetProgress = mProgress;

    private OnProgressDone mOnProgressDone;
    private OnProgressStart mOnProgressStart; // listener for when progress start

    private void initFourthAni() {
        if (mOnProgressStart!=null) {
            mOnProgressStart.progressStart();
        }
    }

    // set current progress
    public void setProgress(float progress) {
        mTargetProgress = progress > 1 ? 1 : progress;
    }

    public void setOnProgressDone(OnProgressDone onProgressDone) {
        mOnProgressDone = onProgressDone;
    }

    public void setOnProgressStart(OnProgressStart onProgressStart) {
        mOnProgressStart = onProgressStart;
    }
    public boolean isProgressDone() {
        return mAniState== AniState.FOURTH_STOP
                || mAniState == AniState.FIFTH_START
                || mAniState == AniState.FIFTH_STOP;
    }

    public void setText(String str) {
        if (str==null || TextUtils.isEmpty(str)) {
            throw new IllegalArgumentException("text can‘t be null or empty");
        }
        mText = str;
    }

    public void reset() {
        mIfReset = true;
    }

    public void setBackColor(int color) {
        mColor = color;
        invalidate();
    }

    // used for fifth animation by time

    private static final long FIFTH_DURATION = 600;
    private long mFifthStartT;
    private long mFifthStopT;

    private void initFifthAni() {
        mFifthStartT = System.currentTimeMillis();
        mFifthStopT = mFifthStartT + FIFTH_DURATION;

    }

    private float getFifthRatio() {
        long now = System.currentTimeMillis();
        if (now >= mFifthStopT) {
            mAniState = AniState.FIFTH_STOP;
            return 1;
        }
        float ratio = (now - mFifthStartT) / (float) FIFTH_DURATION;
        return ratio >= 1 ? 1 : ratio;
    }

    private int getFifthHorizonR() {
        float ratio = getFifthRatio();
        if (ratio==1) {
            return mWidth/2 - PADDING;
        }
        int horizonR = mRadius + (int) (((mWidth/2 - PADDING) - mRadius) * ratio);
        return horizonR;
    }

    private int getFifthColor() {
        float ratio = getFifthRatio();
        if (ratio==1) {
            return mColor;
        }
        int alpha = (int) (ratio * 0xff);
        return Color.argb(alpha, Color.red(mColor), Color.green(mColor), Color.blue(mColor));

    }

    // drawing "correct" sign
    private void drawCorrectSign(Canvas canvas, float ratio) {

        Paint correctPaint = new Paint();
        correctPaint.setAntiAlias(true);
        correctPaint.setStyle(Paint.Style.STROKE);
        correctPaint.setStrokeWidth(10f);
        correctPaint.setColor(0xffffffff);
        correctPaint.setDither(true);
        correctPaint.setStrokeJoin(Paint.Join.ROUND);
        correctPaint.setStrokeCap(Paint.Cap.ROUND);
//        correctPaint.setPathEffect(new CornerPathEffect(5));

        int centerX = mWidth/2;
        int centerY = mHeight/2 + mRadius/4;

        Path path = new Path();
        path.moveTo(centerX - (mRadius*ratio/4), centerY - (mRadius*ratio/4));
        path.lineTo(centerX, centerY);
        path.lineTo(centerX + (mRadius*ratio/2), centerY -(mRadius*ratio/2));

        canvas.drawPath(path, correctPaint);
    }

    public interface OnProgressDone {
        void progressDone();
    }
    public interface OnProgressStart {
        void progressStart();
    }
}

分析:

具体的实现,待会看源码吧,给伸手党几个使用的得到的地方

private static final String TEXT_SUBMIT 初始化看到试图时的那个 String 上图为Submit

操作结束后调用的方法

public interface OnProgressDone {
        void progressDone();
    }

操作开始时调用的方法

 public interface OnProgressStart {
        void progressStart();
    }

控制进度的方法(名为setProgress但是实质上只分1和0,0就停止,1就是继续,并且 无论你传入的参数>1或者等于1都没有区别,如果你开始时传入1,运行过程中不传0,那么动画走完时候就切换到操作成功的界面了。所以需要暂停的话,要在适当的地方调用setProgress(0))

 public void setProgress(float progress) {
        mTargetProgress = progress > 1 ? 1 : progress;
    }

今天一下子看了4篇。。。有点累的。。。下班!!!

源码地址:http://yunpan.cn/cdDeLvTEeQeTk 访问密码 0475

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 08-19

android 自定义View之SubmitView的相关文章

Android自定义View探索(一)—生命周期

Activity代码: public class FiveActivity extends AppCompatActivity { private MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("log", "Activity生命周期:onCreate"); setConte

Android 自定义View视图

创建全新的视图将满足我们独特的UI需求. 本文介绍在指南针开发中会用到的罗盘的界面UI,通过继承View类实现的自定义视图,以此来深刻了解自定义视图. 实现效果图: 源代码: 布局文件activity_main(其中CompassView继承View类): <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.

android自定义View (一)MeasureSpec

A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes: UNSPECIFIED

(转)[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border

Android自定义View——圆形进度条式按钮

介绍 今天上班的时候有个哥们问我怎么去实现一个按钮式的进度条,先来看看他需要实现的效果图. 和普通的圆形进度条类似,只是中间的地方有两个状态表示,未开始,暂停状态.而且他说圆形进度的功能已经实现了.那么我们只需要对中间的两个状态做处理就行了. 先来看看实现的效果图: 上面说了我们只需要处理中间状态的变化就可以了,对于进度的处理直接使用了弘洋文章中实现: http://blog.csdn.net/lmj623565791/article/details/43371299 下面开始具体实现. 具体实

android 自定义View【2】对话框取色&amp;色盘取色的实现

android 自定义View[2]对话框取色&色盘取色的实现    上一篇文章基本介绍了android自定义view的流程:继承view,复写view的一些方法.实现简单的自定义view.这篇文章主要介绍的是系统对话框取色功能,然后顺便介绍升级版,色盘取色[类似于ps中的吸管,对图片点击相应位置,获取那个位置的颜色]. 一.概述:通过该例子了解以下内容: 1.进一步了解android 自定义view. 2.知道如何获取图片上的颜色值. 3.监听屏幕touch,实现移动的时候自动取色.[onDr

Android自定义View(二、深入解析自定义属性)

转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51468648 本文出自:[openXu的博客] 目录: 为什么要自定义属性 怎样自定义属性 属性值的类型format 类中获取属性值 Attributeset和TypedArray以及declare-styleable ??在上一篇博客<Android自定义View(一.初体验)>中我们体验了自定义控件的基本流程: 继承View,覆盖构造方法 自定义属性 重写onMeasure方法测量宽

Android 自定义 view(三)&mdash;&mdash; onDraw

前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自定义view的两个比较重要的方法 onDraw(Canvas canvas) ,在探究 onDraw方法之前,我们必须先深入了解两个类Paint和Canvas .   第一:认识Paint 在探究onDraw之前首先必须要认识两个类,这里给出非常不错的两个资料参考网站,我也是从这里得到想要知道的东西,简单的说

[原] Android 自定义View 密码框 例子

遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 样子 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="border