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

技术是永无止境的,如果真的爱技术,那就勇敢的坚持下去。我很喜欢这句话,当我在遇到问题的时候、当我觉得代码枯燥的时候,我就会问自己,到底是不是真的热爱技术,这个时候,我心里总是起着波澜,我的答案是肯定的,我深深的爱着这门技术。

今天我们继续聊聊Android的自定义View系列。先看看效果吧:

这个是我手机杀毒软件的一个动画效果,类似于雷达搜索,所以用途还是很广泛的,特别是先了解一下这里的具体逻辑和写法,对技术的进步一定很有用。

先简单的分析一下这里的元素,主要有四个圆、一个扇形、还有八条虚线。当知道这些以后,代码就可以开始写了。

先看看这里用到了哪些变量。

    private float mWidth;

    private float mHeight;

    private Paint mPaint, mPaint2, mPaint3, mPaint4, mLinePaint;

    private RectF mRectF;

    private float startAngle = 360;

    private float radius1, radius2, radius3, radius4;
    private Path path;

不是很多,有画笔6个,然后是宽与高,还有就是角度以及四个圆的半径以及一个矩形、当然还有绘制虚线的Path类。接下来是初始化的操作。

    // 初始化画笔操作
    private void initData() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(1);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Style.STROKE);
        mPaint.setColor(Color.parseColor("#ffffff"));

        mPaint2 = new Paint();
        mPaint2.setStrokeWidth(5);
        mPaint2.setColor(Color.parseColor("#00ff00"));

        mPaint3 = new Paint();
        mPaint3.setStrokeWidth(3);
        mPaint3.setAntiAlias(true);
        mPaint3.setStyle(Style.STROKE);
        mPaint3.setColor(Color.parseColor("#ffffff"));

        mPaint4 = new Paint();
        mPaint4.setStrokeWidth(2);
        mPaint4.setAntiAlias(true);
        mPaint4.setStyle(Style.STROKE);
        mPaint4.setColor(Color.parseColor("#ffffff"));

        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setStrokeWidth(1);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setColor(Color.parseColor("#ffffff"));

        path = new Path();

    }

这里包括了五个画笔的初始化操作、一个路径的初始化操作。注意每个画笔的具体样式是不一样的,这样方便实现不同的效果。

初始化的操作完了之后,就是给变量赋值了,还是一样的,我们选择在onSizeChange()里面对变量进行赋值。代码如下:


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getWidth();
        mHeight = getHeight();
        mRectF = new RectF((float) (mWidth * 0.1), (float) (mWidth * 0.1),
                (float) (mWidth * 0.9), (float) (mWidth * 0.9));
        // 绘制渐变效果
        LinearGradient gradient = new LinearGradient((float) (mWidth * 0.3),
                (float) (mWidth * 0.9), (float) (mWidth * 0.1),
                (float) (mWidth * 0.5), new int[] {
                        Color.parseColor("#458EFD"), Color.GREEN,
                        Color.parseColor("#458EFD"), Color.WHITE,
                        Color.parseColor("#458EFD") }, null,
                Shader.TileMode.CLAMP);
        mPaint2.setShader(gradient);
        // 四个圆的半径
        radius1 = (float) (mWidth * 0.4);
        radius2 = (float) (mWidth * 0.3);
        radius3 = (float) (mWidth * 0.2);
        radius4 = (float) (mWidth * 0.1);

    }

其实很明显啊,我们在前面讲的几个自定义的View中,几乎所有的变量初值都是在这个方法里面写的,这个方法究竟有什么特点呢?其实这个是系统回调方法,是系统调用的,它的方法名已经告诉我们了,这个方法会在这个view的大小发生改变是被系统调用,我们要记住的就是view大小变化,这个方法就被执行就可以了。最主要的是,它还在onDraw方法之前调用。

还记得之前的效果吗?里面有一个渐变,这个渐变就是代码里LinearGradient 类的操作结果了,具体的用法,我在后面会专门去解释它。

到了这里,基本上所有的准备工作都完成了,接下来进行真真的绘图。

先看看onDraw方法里面做的操作:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvasArc(canvas);
        canvasArc2(canvas);
        canvasCircle(canvas);
        canvasLine(canvas);

    }

这里有画圆的,有画扇形的,也有画线的,所以可以发现,我们所有看到的效果,都是在onDraw方法里面实现的。我们具体看看每一个方法:

第一个绘制扇形:

    // 绘制旋转的扇形
    private void canvasArc(Canvas canvas) {
        canvas.drawArc(mRectF, startAngle, 60, true, mPaint2);
    }

只有两行代码,也很容易理解,那么第二个扇形也是差不多:

    // 绘制旋转的扇形
    private void canvasArc2(Canvas canvas) {
        canvas.drawArc(mRectF, startAngle, 1, true, mPaint3);
    }

值得注意的是,第二个的扇形的角度是1,为什么是1呢,原来我这里只是想要它旋转角度的效果,并不需要它有多宽,所以在具体实现自定义View的时候,也要学会活学活用。

两个扇形已经画好了,那就看看四个圆怎么画:

    // 绘制四个圆
    private void canvasCircle(Canvas canvas) {
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius1, mPaint3);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius2, mPaint);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius3, mPaint);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius4, mPaint4);
    }

四个圆就是四行代码,怎么样,很简单吧?

注意他们所用的画笔是不太一样的,这里是比较容易忽视的地方。

最后来看看我纠结很久的画虚线的操作,这里真是把我卡了好一会儿,先看看代码:


    // 绘制虚线
    private void canvasLine(Canvas canvas) {
        int lineCount = 8;
        for (int i = 0; i < lineCount; i++) {
            path.moveTo(mWidth / 2, mHeight / 2);
            path.lineTo(radius1, radius4);
            PathEffect effects = new DashPathEffect(new float[] {
                    (float) (mWidth * 0.005), (float) (mWidth * 0.02),
                    (float) (mWidth * 0.005), (float) (mWidth * 0.02) }, 0);
            mLinePaint.setPathEffect(effects);
            canvas.drawPath(path, mLinePaint);
            canvas.rotate(45, mWidth / 2, mHeight / 2);
        }

    }

按理说,绘制虚线,本质也是画线,但是我一开始是用canvas.drawLine方法,但是没有效果,网上查找了资料,才知道可能是版本的问题,于是我换用了path。

到了这个时候,全部效果已经出来了,那么为了让她动起来,我们还是要加点逻辑,我的思路是这样的:

定义一个线程,然后通过改变扇形的开始角度来实现动画的效果。

代码如下:

class MyThread extends Thread {

        @Override
        public void run() {

            while (true) {
                if (running) {
                    SystemClock.sleep(200);
                    handler.sendEmptyMessage(2);
                }
            }

        }
    }

    private boolean running = true;

    public Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            synchronized (this) {
                if (startAngle < 1) {
                    startAngle = 360;
                } else {
                    startAngle--;
                    invalidate();
                }
            }
        };

    };

到了这里,基本可以实现动画效果了,为了让控件更好的被外面所用,我们还像外面暴露了一些方法。

    // 开启动画
    public void setStartAngle() {
        thread = new MyThread();
        thread.start();

    }

    // 重新开启动画
    public void startAnge() {
        running = true;
    }

    // 暂停动画
    public void stopAnge() {
        running = false;
    }

    // 是否在运动
    public boolean isRunning() {
        return running;
    }

最后看看我在MainActivity.java的操作:

/**
 * 小瓶盖 2016年7月12日16:35:26
 *
 * @author 自定义View——仿Vivo i管家病毒扫描动画效果
 *
 *         博客地址:http://blog.csdn.net/qq_25193681
 */
public class MainActivity extends ActionBarActivity {
    private VirusKilling mVirusKilling;
    float mVirusKillingValue = 360;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        mVirusKilling = (VirusKilling) findViewById(R.id.Virus_Killing);

        tv = (TextView) findViewById(R.id.tv);
        // 传入TextView,改变下方的文字的时候用到
        mVirusKilling.setTextView(tv);
        // 第一次打开的时候就开始更新动画
        mVirusKilling.setStartAngle();

    }

    /**
     * 点击事件的处理
     *
     * @param v
     */
    public void onStop(View v) {
        TextView tv = (TextView) v;
        if (mVirusKilling.isRunning()) {
            tv.setText("继续扫描");
            mVirusKilling.stopAnge();
        } else {
            tv.setText("停止扫描");
            mVirusKilling.startAnge();
        }

    }

    @Override
    protected void onPause() {
        mVirusKilling.stopAnge();
        super.onPause();

    };

    @Override
    protected void onStart() {
        mVirusKilling.startAnge();
        super.onStart();

    }
}

还有布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context="com.ltl.vivoviruskilling.MainActivity" >

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#458EFD"
    android:orientation="vertical" >

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="40dip"
    android:padding="8dip" >

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/back"
      />

     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerInParent="true"
         android:text="病毒查杀"
         android:textColor="#ffffff"
         android:textSize="20sp" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentRight="true"
           android:text="设置"
           android:textColor="#ffffff"
           android:textSize="20sp" />

</RelativeLayout>

     <com.ltl.vivoviruskilling.view.VirusKilling
         android:id="@+id/Virus_Killing"
         android:layout_width="200dip"
         android:layout_height="200dip"
         android:layout_gravity="center" />

     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" >

       <TextView
           android:onClick="onStop"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center"
           android:text="停止扫描"
           android:paddingTop="5dip"
           android:paddingBottom="5dip"
           android:paddingLeft="20dip"
           android:paddingRight="20dip"
           android:background="@drawable/btn_bg"
           android:textColor="#ffffff"
           android:textSize="18sp" />

       <TextView
           android:id="@+id/tv"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="正在扫描:NETWORKSTATE"
           android:layout_margin="20sp"
           android:textColor="@android:color/holo_blue_bright"
           android:textSize="16sp" />

</LinearLayout>

</LinearLayout>

</RelativeLayout>

一个背景效果的代码:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <corners android:radius="30dip"/>
    <solid android:color="#00000000"/>

<stroke
    android:width="1dip"
    android:color="#ffffff" />

</shape>

到现在,所有的效果都出来了,可能讲的不是很详细,但是代码是没有问题的,代码也不多,多看几次也很容易理解。

最后我把源码贴上来:源码地址

现在还是开始写博客,技术可能不是很高端,但是日子还长,期待我们一起进步。

时间: 2024-08-01 19:49:15

Android自定义View——仿vivo i管家病毒扫描动画效果的相关文章

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

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

【Android自定义View实战】之仿百度加载动画,一种优雅的Loading方式

转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53470872 本文出自[DylanAndroid的博客] Android自定义View实战之仿百度加载动画一种优雅的Loading方式 第一个仿百度加载动画用ObjectAnimator属性动画操作ImageView的属性方法实现 第二个仿百度加载动画第二种实现方式用ValueAnimator原生的ondraw方法实现 第三个扔球动画-水平旋转动画 第四个扔球动画-垂直旋转动

android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索

我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的Android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体上来说,左边是一个ListView,右边是一个自定义View,但是左边的ListView和我们平常使用的ListView还有一点点不同,就是在ListView中我对所有的联系人进行了分组,那么这种效果的实现最常见的就是两种思路: 1.使用ExpandableListView来实现这种分组效果 2.使

android自定义View之(六)------高仿华为荣耀3C的圆形刻度比例图(ShowPercentView)

为什么写这篇文章: 显示当前的容量所占的比例,表现当前计划的进度,一般都会采用百分比的方式,而图形显示,以其一目了然的直观性和赏心悦目的美观形成为了我们的当然的首选. 在图形表示百分比的方法中,我们有用画圆的圆形进度条的方法<<android自定义View之(二)------简单进度条显示样例篇>>,也有用画弧形的进度条的方法<<android自定义View之(三)------视频音量调控样例>>,今天看到华为荣耀3C的一个界面: 个人觉得这个表示比例的圆形

ANDROID自定义视图——仿瀑布布局(附源码)

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():如何绘制这个View. 第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了. 第一步的测量,可以参考:(ANDROID自定义视图--onMeasure,MeasureSpec源码 流程 思路详解) 第二步的布局,可以参考:(AN

android自定义View之NotePad出鞘记

现在我们的手机上基本都会有一个记事本,用起来倒也还算方便,记事本这种东东,如果我想要自己实现,该怎么做呢?今天我们就通过自定义View的方式来自定义一个记事本.OK,废话不多说,先来看看效果图. 整个页面还是很简单的. 1.自定义View的分类 OK,那么在正文开始之前,我想先来说说自定义View的分类,自定义View我们一共分为三类 1.自绘控件 自绘控件就是我们自定义View继承自已有控件,然后扩展其功能,之前两篇自定义View的博客(android自定义View之钟表诞生记,android

Android 自定义View合集

自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/Mr-XiaoLiang 自定义控件三部曲 http://blog.csdn.net/harvic880925?viewmode=contents Android 从0开始自定义控件之View基础知识与概念 http://blog.csdn.net/airsaid/article/details/5

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.