Android_自定义view动画按钮

昨天偶偶然看见UI 给的一个交互的效果,原图如下

就是下面的loginbutton,于是大概模仿了一下,

并没有做这个UI的全部效果,有兴趣的可以完善后面展开的效果

下面是demo的button效果

这个View用到的知识点比较简单:

  1. view的坐标系知识,(大家没有不熟悉的吧)
  2. view的canvas基本API(画矩形,画扇形,)
  3. view的自定义属性(attr提供选项)
  4. 属性动画的知识(老生常谈的知识,ObjectAnimation和ValueAniamtion)

下面我们就一步步实现这个button

  • 我们写一个自定义的类继承View实现其构造,在构造函数中获取自定义属性的值
 public ATLoginButton(Context context) {
        this(context, null);
    }

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

    public ATLoginButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 获取自定义属性集合
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ATLoginButton, defStyleAttr, R.style.def_button_style);
        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ATLoginButton_button_color:
                    buttonColor = typedArray.getColor(attr, getResources().getColor(R.color.colorAccent));
                    break;
                case R.styleable.ATLoginButton_text_color:
                    textColor = typedArray.getColor(attr, getResources().getColor(R.color.colorW));
                    break;
                case R.styleable.ATLoginButton_text_size:
                    textSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ATLoginButton_login_text:
                    loginDesc = typedArray.getString(attr);
                    break;
                case R.styleable.ATLoginButton_failed_text:
                    failDesc = typedArray.getString(attr);
                    break;
                case R.styleable.ATLoginButton_circle_loading_color:
                    circlerLoadingColor = typedArray.getColor(attr, Color.GRAY);
                    break;
                case R.styleable.ATLoginButton_circle_loading_width:
                    circleLoadingLineWidth = typedArray.getDimensionPixelOffset(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ATLoginButton_failed_button_color:
                    failedButtonColor = typedArray.getColor(attr, Color.GRAY);
                    break;
            }
        }
        typedArray.recycle();
        init();
    }
  • 重写view的onMeasue,确定和测量我们view的大小和测试模式的确定
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// 主要view支持wrap_content属性,如果不处理,warpcontent和matchparent感官给我们的感觉是一样的,其实并不然,想了解的可以看下官方文档
        int widthSize;
        int heightSize;

        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
            widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_W, getResources().getDisplayMetrics());
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }

        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_H, getResources().getDisplayMetrics());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  • 然后获取测量后view的宽和高
  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        circleAndRoundSize = h / 2;
        textPoint = getTextPointInView(loginDesc);
        buttonRectF = new RectF(0, 0, mWidth, mHeight);
        mText = loginDesc;
        viewState = LoginViewState.NORMAL_STATE;
    }
  • 然后就是最后一步了onDraw,几分钟,我们已经完成了百分之80的工作

    最后20%就是让view的内容画到画布上,并且让其动起来就ok了

  • 画圆形的button,注意这个圆角button,动起来的时候量个半圆需要合并成一个完整的圈,所以倒角的半径就已经确定了,就是我们view高度的一半,这里需要注意下
//画button代码
 private void drawButton(Canvas canvas) {
        canvas.drawRoundRect(buttonRectF, circleAndRoundSize, circleAndRoundSize, buttonPaint);
    }
  • 画button上面的文字
 private void drawTextDesc(Canvas canvas, String textDesc) {
        canvas.drawText(textDesc, textPoint.x, textPoint.y, textPaint);
    }
  • 小插曲,我们在绘制文字的时候为了让文字居中,我们需要获取文字测量后的信息如下
// 这里我直接获取了文字的宽高然后把文字在view中的坐标信息计算并返回出去了
 private Point getTextPointInView(String textDesc) {
        Point point = new Point();
        int textW = (mWidth - (int) textPaint.measureText(textDesc)) / 2;
        Paint.FontMetrics fm = textPaint.getFontMetrics();
        int textH = (int) Math.ceil(fm.descent - fm.top);
        point.set(textW, (mHeight + textH) / 2);
        return point;
    }
  • 画扇形的方法,这个方形就是我们那个loading的圆圈
  private void drawCircleLoading(Canvas canvas) {
        float circleSpacing = circleAndRoundSize / 4;
        float x = (mHeight - 10) / 2;
        float y = (mHeight - 10) / 2;
        canvas.translate(mWidth / 2, y);
        canvas.scale(1F, 1F);
        canvas.rotate(0);
        RectF rectF = new RectF(-x + circleSpacing, -y + circleSpacing, x - circleSpacing, y - circleSpacing);
        canvas.drawArc(rectF, -45, 270, false, circleLoadingPaint);
    }
  • ok到现在我们所有的图形元素都准备到位,剩下的就是提供两个方法,一个是开始登陆,button变成圆形,还有一个就是登陆的结果不管失败还是成功都要变成button,以及还有一个在变成圆球的时候旋转的动画

一步步来

    public void buttonLoginAction() {
        setClickable(false);
        buttonPaint.setColor(buttonColor);
        if (viewState != LoginViewState.NORMAL_STATE) {
            circleLoadingPaint.setColor(circlerLoadingColor);
        }
        ValueAnimator valueAnimator = getValA(0F, 1F);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
            // 这里是我们根据valueani返回的比例,确定button的新的left和right
                Float aFloat = Float.valueOf(valueAnimator.getAnimatedValue().toString());
                int left = (int) ((aFloat) * mWidth);
                int jL = mWidth / 2 - circleAndRoundSize;
                if (left >= jL) {
                //由于float不好做比价所以转成int,如果新的left坐标大于view的测量一半说明这个时候应该变成圆形了,
                // 我们手动让其变成正规圆,抛弃float带来的误差
                    buttonRectF = new RectF(jL, 0, jL + mHeight, mHeight);
                    textPaint.setColor(Color.TRANSPARENT);
                    isLoading = true;
                    // 动画取消
                    invalidate();
                    valueAnimator.cancel();
                    startLoading();
                    viewState = LoginViewState.LOADING_STATE;
                    return;
                }
                float right = (1 - aFloat) * mWidth;
                buttonRectF = new RectF(left, 0, right, mHeight);
                invalidate();
            }
        });
        valueAnimator.start();
    }
  • 然后就是类似的一个方法,圆圈变成button的方法
    public void buttonLoaginResultAciton(final boolean isSuccess) {
        viewState = isSuccess ? LoginViewState.SUCCESS_STATE : LoginViewState.FAILED_STATE;
        stopLoading();
        ValueAnimator valueAnimator = getValA(0F, 1F);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
            //依然是计算新的left
                int jL = mWidth / 2 - circleAndRoundSize;
                Float aFloat = Float.valueOf(valueAnimator.getAnimatedValue().toString());
                int left = (int) ((1 - aFloat) * jL);
                float right = jL + mHeight + jL * aFloat;
                buttonRectF = new RectF(left, 0, right, mHeight);
                textPaint.setColor(textColor);
                if (isSuccess){
                // 登陆成功,重置view的状态
                    mText = loginDesc;
                    textPoint = getTextPointInView(mText);
                    buttonPaint.setColor(buttonColor);
                    invalidate();
                    if (aFloat.intValue() == 1) {
                        setClickable(true);
                        buttonRectF = new RectF(0, 0, mWidth, mHeight);
                        invalidate();
                        valueAnimator.cancel();
                    }
                }else {
                // 登陆失败,进入颤抖动画显示失败的文字和背景
                    mText = failDesc;
                    textPoint = getTextPointInView(mText);
                    buttonPaint.setColor(failedButtonColor);
                    invalidate();
                    if (aFloat.intValue() == 1) {
                        setClickable(true);
                        buttonRectF = new RectF(0, 0, mWidth, mHeight);
                        invalidate();
                        shakeFailed();
                        valueAnimator.cancel();
                    }
                }
            }
        });
        valueAnimator.start();
    }

这样我们view的全部工作都做完了,剩下的就是在Mainactivity里面用一下

    private void addListener2Button(final ATLoginButton atLoginButton, final boolean loaginStatus) {
        atLoginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 这里是登陆动画,然后去请求服务器接口
                atLoginButton.buttonLoginAction();
                // 加入三秒后,登陆失败或者成功
                atLoginButton.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //这里调用View获取登陆状态的方法,成功或者失败
                        atLoginButton.buttonLoaginResultAciton(loaginStatus);
                        String notice = loaginStatus ? "登陆成功,重置button状态" : "登录失败,显示失败状态";
                        Toast.makeText(getApplicationContext(), notice, Toast.LENGTH_SHORT).show();
                    }
                }, 3000);
            }
        });
    }

由于 就一个这个demo就一个自定义view,项目就不上传了,把完整的代码给大家,有兴趣的可以放到AS里面跑一下,谢谢!

最后给大家推荐我的一个比价完整的开源项目,SoHOT链接如下,

文章末尾有Githup免费下载地址,希望star谢谢

public class ATLoginButton extends View {
    private static final float DE_W = 280.F;
    private static final float DE_H = 65.F;
    private static final long ANIMATION_TIME = 800;
    private int buttonColor;
    private int textColor;
    private int textSize;
    private int circlerLoadingColor;
    private int failedButtonColor;
    private int circleLoadingLineWidth;
    private String loginDesc;
    private String failDesc;
    private String mText;
    private Paint buttonPaint;
    private Paint textPaint;
    private Paint circleLoadingPaint;

    private int mHeight;
    private int mWidth;
    private int circleAndRoundSize;

    private RectF buttonRectF;
    private Point textPoint;
    private boolean isLoading;
    private RotateAnimation rotateAnimation;

    private int viewState;

    public ATLoginButton(Context context) {
        this(context, null);
    }

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

    public ATLoginButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ATLoginButton, defStyleAttr, R.style.def_button_style);
        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ATLoginButton_button_color:
                    buttonColor = typedArray.getColor(attr, getResources().getColor(R.color.colorAccent));
                    break;
                case R.styleable.ATLoginButton_text_color:
                    textColor = typedArray.getColor(attr, getResources().getColor(R.color.colorW));
                    break;
                case R.styleable.ATLoginButton_text_size:
                    textSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ATLoginButton_login_text:
                    loginDesc = typedArray.getString(attr);
                    break;
                case R.styleable.ATLoginButton_failed_text:
                    failDesc = typedArray.getString(attr);
                    break;
                case R.styleable.ATLoginButton_circle_loading_color:
                    circlerLoadingColor = typedArray.getColor(attr, Color.GRAY);
                    break;
                case R.styleable.ATLoginButton_circle_loading_width:
                    circleLoadingLineWidth = typedArray.getDimensionPixelOffset(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ATLoginButton_failed_button_color:
                    failedButtonColor = typedArray.getColor(attr, Color.GRAY);
                    break;
            }
        }
        typedArray.recycle();
        init();
    }

    private void init() {
        buttonPaint = creatPaint(buttonColor, 0, Paint.Style.FILL, circleLoadingLineWidth);
        circleLoadingPaint = creatPaint(circlerLoadingColor, 0, Paint.Style.STROKE, circleLoadingLineWidth);
        textPaint = creatPaint(textColor, textSize, Paint.Style.FILL, circleLoadingLineWidth);
    }

    private Paint creatPaint(int paintColor, int textSize, Paint.Style style, int lineWidth) {
        Paint paint = new Paint();
        paint.setColor(paintColor);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(lineWidth);
        paint.setDither(true);
        paint.setTextSize(textSize);
        paint.setStyle(style);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeJoin(Paint.Join.ROUND);
        return paint;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize;
        int heightSize;

        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
            widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_W, getResources().getDisplayMetrics());
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }

        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_H, getResources().getDisplayMetrics());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        circleAndRoundSize = h / 2;
        textPoint = getTextPointInView(loginDesc);
        buttonRectF = new RectF(0, 0, mWidth, mHeight);
        mText = loginDesc;
        viewState = LoginViewState.NORMAL_STATE;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawButton(canvas);
        drawTextDesc(canvas, mText);
        if (isLoading) {
            drawCircleLoading(canvas);
        }
    }

    private void drawCircleLoading(Canvas canvas) {
        float circleSpacing = circleAndRoundSize / 4;
        float x = (mHeight - 10) / 2;
        float y = (mHeight - 10) / 2;
        canvas.translate(mWidth / 2, y);
        canvas.scale(1F, 1F);
        canvas.rotate(0);
        RectF rectF = new RectF(-x + circleSpacing, -y + circleSpacing, x - circleSpacing, y - circleSpacing);
        canvas.drawArc(rectF, -45, 270, false, circleLoadingPaint);
    }

    private void drawTextDesc(Canvas canvas, String textDesc) {
        canvas.drawText(textDesc, textPoint.x, textPoint.y, textPaint);
    }

    private void drawButton(Canvas canvas) {
        canvas.drawRoundRect(buttonRectF, circleAndRoundSize, circleAndRoundSize, buttonPaint);
    }

    private Point getTextPointInView(String textDesc) {
        Point point = new Point();
        int textW = (mWidth - (int) textPaint.measureText(textDesc)) / 2;
        Paint.FontMetrics fm = textPaint.getFontMetrics();
        int textH = (int) Math.ceil(fm.descent - fm.top);
        point.set(textW, (mHeight + textH) / 2);
        return point;
    }

    public void buttonLoginAction() {
        setClickable(false);
        buttonPaint.setColor(buttonColor);
        if (viewState != LoginViewState.NORMAL_STATE) {
            circleLoadingPaint.setColor(circlerLoadingColor);
        }
        ValueAnimator valueAnimator = getValA(0F, 1F);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float aFloat = Float.valueOf(valueAnimator.getAnimatedValue().toString());
                int left = (int) ((aFloat) * mWidth);
                int jL = mWidth / 2 - circleAndRoundSize;
                if (left >= jL) {
                    buttonRectF = new RectF(jL, 0, jL + mHeight, mHeight);
                    textPaint.setColor(Color.TRANSPARENT);
                    isLoading = true;
                    invalidate();
                    valueAnimator.cancel();
                    startLoading();
                    viewState = LoginViewState.LOADING_STATE;
                    return;
                }
                float right = (1 - aFloat) * mWidth;
                buttonRectF = new RectF(left, 0, right, mHeight);
                invalidate();
            }
        });
        valueAnimator.start();
    }

    public void buttonLoaginResultAciton(final boolean isSuccess) {
        viewState = isSuccess ? LoginViewState.SUCCESS_STATE : LoginViewState.FAILED_STATE;
        stopLoading();
        ValueAnimator valueAnimator = getValA(0F, 1F);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int jL = mWidth / 2 - circleAndRoundSize;
                Float aFloat = Float.valueOf(valueAnimator.getAnimatedValue().toString());
                int left = (int) ((1 - aFloat) * jL);
                float right = jL + mHeight + jL * aFloat;
                buttonRectF = new RectF(left, 0, right, mHeight);
                textPaint.setColor(textColor);
                if (isSuccess){
                    mText = loginDesc;
                    textPoint = getTextPointInView(mText);
                    buttonPaint.setColor(buttonColor);
                    invalidate();
                    if (aFloat.intValue() == 1) {
                        setClickable(true);
                        buttonRectF = new RectF(0, 0, mWidth, mHeight);
                        invalidate();
                        valueAnimator.cancel();
                    }
                }else {
                    mText = failDesc;
                    textPoint = getTextPointInView(mText);
                    buttonPaint.setColor(failedButtonColor);
                    invalidate();
                    if (aFloat.intValue() == 1) {
                        setClickable(true);
                        buttonRectF = new RectF(0, 0, mWidth, mHeight);
                        invalidate();
                        shakeFailed();
                        valueAnimator.cancel();
                    }
                }
            }
        });
        valueAnimator.start();
    }

    private void stopLoading() {
        isLoading = false;
        circleLoadingPaint.setColor(Color.TRANSPARENT);
        if (null != rotateAnimation) {
            rotateAnimation.cancel();
        }
    }
    private void shakeFailed() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "translationX", 0, 10);
        objectAnimator.setDuration(500);
        objectAnimator.setInterpolator(new CycleInterpolator(10));
        objectAnimator.start();
    }

    private void startLoading() {
        rotateAnimation = new RotateAnimation(0F, 360F, mWidth / 2, mHeight / 2);
        rotateAnimation.setDuration(500);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        rotateAnimation.setInterpolator(new LinearInterpolator());
        startAnimation(rotateAnimation);
    }

    private static class LoginViewState {
        static final int NORMAL_STATE = 90;
        static final int LOADING_STATE = 91;
        static final int FAILED_STATE = 92;
        static final int SUCCESS_STATE = 93;
    }
}
时间: 2024-10-23 15:36:33

Android_自定义view动画按钮的相关文章

Android_自定义View之跳动的loading

这篇文章记录一下Android自定义UI的知识,一个小小的跳动的loading,特别感谢Nil的指点-废话不多,先来效果 SoHOT链接和star地址:SoHOT源码地址,在文章最后最后有github开源地址,别错过 如果您还没有去给SoHOT一颗star而直接看这个项目,那简直是有点损失,希望您点上面的链接,去star和下载体验一下SoHoT,捡起那个大西瓜再来捡这个小芝麻项目,你懂的!! 凑乎看吧,录制的效果不如真机上平滑-凑乎看吧. 1,自定义View的自定义属性提取 2,View中动画的

Android动画——View动画

一.概述 View动画共有四种效果分别是平移动画.旋转动画.缩放动画以及透明度变化,此外帧动画也属于View动画,但实现效果与前面几种有所区别. 二.View动画的种类 四种动画分别对应四个Animation的子类TranslateAnimation.RotateAnimation.ScaleAnimation.AlphaAnimation,同时也可用XML来定义动画,分别对应标签<translate> <rotate> <scale> <alpha>,在实

自定义View Controller转换动画

原文链接 : Introduction to Custom View Controller Transitions and Animations 原文作者 : joyce echessa 译文出自 : 开发技术前线 www.devtf.cn 译者 : kmyhy 观察 iOS 自带的 App,你会看到当你从一个视图导航到另一个视图时总是会显示各种各样的转换动画,以"主-从"视图为例(类似的程序有Messages App或者系统设置程序),一个轻扫动作能够让详情视图呈现在主视图之上,在呈

Android自定义view教程04-------------自定义属性动画

不太会美工,所以随便写了个菜单打开关闭的动画,主要是教会大家如何使用属性动画,可以这么说 学会属性动画 前面的fream动画和tween动画可以不用看了,因为他们2能做的,属性动画也能做, 他们2不能做的,属性动画也能做. 直接上代码吧,注释写的还算详细. 主activity代码 实际上没啥好看的,主要就是使用了dialogfragment,没有用dialog,因为谷歌后来推荐 我们使用这个dialogfragment,而且这个确实比dialog要优秀方便很多. package com.exam

Android开发之自定义View-可动画展开收缩View的实现

有时候需要点击一个view可以动画展开和收缩折叠一个View这样的效果,这样就可以直接自定义View来实现. 本例中,采用继承FrameLayout来实现自定义的ExpandView.下面将详细介绍各个部分来实现该类以及如何使用该自定义视图. 效果图如下: 未展开效果: 正在向上折叠收缩中的效果: 已经展开效果: 自定义展开类:ExpandView的实现: package com.czm.customview; import android.content.Context; import and

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

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

Android属性动画与自定义View——实现vivo x6更新系统的动画效果

晚上好,现在是凌晨两点半,然后我还在写代码.电脑里播放着<凌晨两点半>,晚上写代码,脑子更清醒,思路更清晰. 今天聊聊属性动画和自定义View搭配使用,前面都讲到自定义View和属性动画,但是一起用的还是不多,刚巧今晚手机提示我更新系统,我看到那个更新的动画还不错,仔细的分析了一下,于是我也决定写一个,不是一模一样的,但是效果和原理是一样的. 先看看图: 这是一张静态的图,这里有三个波浪线,当下载完之后,波浪线会往上活动,一直到消失. 所以难点也是在这个波浪线上.这个波浪线类似于一个水波纹,也

Android自定义View——仿vivo i管家病毒扫描动画效果

技术是永无止境的,如果真的爱技术,那就勇敢的坚持下去.我很喜欢这句话,当我在遇到问题的时候.当我觉得代码枯燥的时候,我就会问自己,到底是不是真的热爱技术,这个时候,我心里总是起着波澜,我的答案是肯定的,我深深的爱着这门技术. 今天我们继续聊聊Android的自定义View系列.先看看效果吧: 这个是我手机杀毒软件的一个动画效果,类似于雷达搜索,所以用途还是很广泛的,特别是先了解一下这里的具体逻辑和写法,对技术的进步一定很有用. 先简单的分析一下这里的元素,主要有四个圆.一个扇形.还有八条虚线.当

Android 自定义View修炼-自定义加载进度动画LoadingImageView

一.概述 本自定义View,是加载进度动画的自定义View,继承于ImageView来实现,主要实现蒙层加载进度的加载进度效果. 支持水平左右加载和垂直上下加载四个方向,同时也支持自定义蒙层进度颜色. 直接看下面的效果图吧. 二.效果图 废话不说,先来看看效果图吧~~ 三.实现原理方案 1.自定义View-XCLoadingImageView,继承ImageVIew来实现,这样就不用自己再处理drawable和测量的工作内容. 2.根据蒙层颜色创建一个蒙层bitmap,然后根据这个bitmap来