超酷的计步器APP(一)——炫酷功能实现,自定义水波纹特效、自定义炫酷开始按钮、属性动画的综合体验

来源:互联网 发布:上海恺英网络知乎 编辑:程序博客网 时间:2024/04/28 04:54

超酷的计步器APP(一)——炫酷功能实现,自定义水波纹特效、自定义炫酷开始按钮、属性动画的综合体验

好久没写博客了,没给大家分享技术了,真是有些惭愧。这段时间我在找工作,今年Android的行情也不怎么好,再加上我又是一个应届生,所以呢,更是不好找了。但是我没有放弃,经过自己的不懈努力,还是找到了自己喜欢的Android工作,心里的一块石头终于落下了。但是迎接我来的是更多的挑战,我喜欢那种不断的挑战自我,在困难中让自己变得更强大的感觉。相信阳光总在风雨后,因为每一个你不满意的现在,都有一个你没有努力的曾经。所以我们一起努力,为了生活,生下来、活下去。


今天我们来一起学习一个很酷的小项目——计步器。
关于这个项目我想分两篇博客来和大家分享,一篇先来学习界面和功能中用到的自定义控件,下篇来实现动画和计步器连接起来开始计算步数的效果
我们先来看效果图。

这里写图片描述

是不是很酷很炫呢,哈哈,我觉得酷酷哒。
那么我们今天先来学习那个很酷的效果图的实现。
点击开始按钮,出现结束和暂停按钮,开始按钮隐藏,横线变波浪的效果

我们先新建一个Android项目,开始在main_activity.xml中书写布局
先来看这个布局的Component Tree
这里写图片描述
我们来写他的布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/bg4_dark_blackish_green"    >    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@drawable/activity_walking_backgroud"        android:orientation="vertical">        <include layout="@layout/top_bar" />        <TextView            android:id="@+id/step_count"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:layout_marginTop="90dp"            android:textColor="@color/white"            android:textSize="70sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:layout_marginTop="18dp"            android:text="计算步数"            android:textColor="@color/white"            android:textSize="13sp" />        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginBottom="35dp"            android:layout_marginLeft="30dp"            android:layout_marginRight="50dp"            android:layout_marginTop="60dp"            android:orientation="horizontal"            android:weightSum="2">            <RelativeLayout                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_weight="1">                <ImageView                    android:id="@+id/iv_calories"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_alignParentLeft="true"                    android:src="@mipmap/calories" />                <TextView                    android:id="@+id/calories"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_alignParentRight="true"                    android:layout_toRightOf="@id/iv_calories"                    android:textColor="@color/white"                    android:textSize="24sp" />                <TextView                    android:id="@+id/tv_calories"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_below="@id/calories"                    android:layout_toRightOf="@id/iv_calories"                    android:text="热量:千卡"                    android:textColor="@color/white"                    android:textSize="10sp" />            </RelativeLayout>            <RelativeLayout                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_weight="1">                <ImageView                    android:id="@+id/iv_time"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_centerHorizontal="true"                    android:src="@mipmap/time" />                <TextView                    android:id="@+id/time"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_alignParentRight="true"                    android:layout_toRightOf="@id/iv_time"                    android:text="0"                    android:textColor="@color/white"                    android:textSize="24sp" />                <TextView                    android:id="@+id/tv_time"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:layout_below="@id/time"                    android:layout_toRightOf="@id/iv_time"                    android:text="时间:分钟"                    android:textColor="@color/white"                    android:textSize="10sp" />            </RelativeLayout>        </LinearLayout>    </LinearLayout>    <com.adu.running.view.WaveView        android:id="@+id/wave_view"        android:layout_width="match_parent"        android:layout_height="40dp"        android:layout_marginTop="-20dp"        />    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_marginLeft="30dp"        android:layout_marginRight="30dp"        android:layout_marginTop="27dp">        <com.adu.running.view.CircleButton            android:id="@+id/stop"            android:layout_width="94dp"            android:layout_height="94dp"            android:layout_alignParentLeft="true"            android:layout_centerVertical="true"            android:layout_gravity="center"            android:text="结束"            android:textColor="@color/white"            android:visibility="gone"/>        <com.adu.running.view.CircleWaveButton            android:id="@+id/start"            android:layout_width="94dp"            android:layout_height="94dp"            android:layout_centerInParent="true"            android:textColor="@color/white"/>        <com.adu.running.view.CircleButton            android:id="@+id/bt_continue"            android:layout_width="94dp"            android:layout_height="94dp"            android:layout_alignParentRight="true"            android:layout_centerVertical="true"            android:layout_gravity="center"            android:text="暂停"            android:textColor="@color/white"            android:visibility="gone"/>        </RelativeLayout></LinearLayout>

如果你直接复制上面的代码到你的项目中肯定会报错的,因为上面的布局中我们引用了一个自定义的toolbar布局和几个颜色值,还有几个自定义View
一会我会把代码给大家传上去,或者直接看我GitHub上面的源码

我们现在看下面的三个自定义View怎么来完成的

WaveView.java自定义波纹类

package com.adu.running.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Path;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.view.View;/** * Created by adu on 2016/10/30. */public class WaveView extends View {    //波纹颜色    private int waveColor = 0xff0099CC;    // 振幅    private float swing = 0;    private int height;    private int width;    private int ms = 30;    private float isPause=0f;    private boolean isRun = false;    //绘制波纹的画笔    private Paint wavePaint;    //Path类可以预先在View上将N个点连成一条"路径"    // 然后调用Canvas的drawPath(path,paint)即可沿着路径绘制图形    private Path path1;    private Path path2;    private Path path3;    public WaveView(Context context) {        super(context);        init();    }    public WaveView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        // 初始绘制波纹的画笔        wavePaint = new Paint();        // 去除画笔锯齿        wavePaint.setAntiAlias(true);        //设置画笔颜色        wavePaint.setColor(waveColor);        //设置线宽        wavePaint.setStrokeWidth(5);        //设置风格为空心        wavePaint.setStyle(Paint.Style.STROKE);        path1 = new Path();        path2 = new Path();        path3 = new Path();    }    /**     * 计算view高度宽度大小     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        initLayoutParams();    }    private void initLayoutParams() {        height = this.getHeight();        width = this.getWidth();    }    @Override protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        setPath();        wavePaint.setStrokeWidth(6);        wavePaint.setAlpha(100);//设置透明度        canvas.drawPath(path1, wavePaint);        wavePaint.setStrokeWidth(3);        wavePaint.setAlpha(80);        canvas.drawPath(path2, wavePaint);        wavePaint.setAlpha(60);        canvas.drawPath(path3, wavePaint);    }    /**     * 设置三个线条上下震动的振幅     */    private void setPath() {        int x = 0;        int y = 0;        path1.reset();//清除掉path里的线条和曲线        for (int i = 0; i < width; i++) {            x = i;            y = (int) (isPause*40* Math.sin(i * 2*0.7f * Math.PI / width+swing) + height*0.5);            if (i == 0) {                path1.moveTo(x, y);//定位绘画开始位置            }            //绘制圆滑曲线,即贝塞尔曲线;(x, y)为控制点,(x + 1, y)为结束点            path1.quadTo(x, y, x + 1, y);        }        path2.reset();        for (int i = 0; i < width; i++) {            x = i;            y = (int) (isPause*40* Math.sin(i * 2*0.7f * Math.PI / width+swing+0.3f) + height*0.5);            if (i == 0) {                path2.moveTo(x, y);            }            path2.quadTo(x, y, x + 1, y);        }        path3.reset();        for (int i = 0; i < width; i++) {            x = i;            y = (int) (isPause*40* Math.sin(i * 2*0.7f * Math.PI / width+swing+0.3f) + height*0.5);            if (i == 0) {                path3.moveTo(x, y);            }            path3.quadTo(x, y, x + 1, y);        }        path1.close();//回到初始点形成封闭的曲线        path2.close();        path3.close();    }    public void start(){        this.isRun=true;        this.isPause=1.0f;        new MyThread().start();//让波纹在子线程中运行    }    public void stop(){        this.isRun=false;        this.isPause=0.0f;        invalidate();//请求重新绘制的界面    }    private class MyThread extends Thread {        @Override        public void run() {            while (isRun) {                swing+=-0.25f;                mHandler.sendEmptyMessage(1);                try {                    Thread.sleep(ms);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 1) {                invalidate();//请求重新绘制的界面            }        }    };}

让我们在MainActivity的布局中新建两个Button按钮开对自定义波纹类进行测试,分别调用它的start( )和stop( )方法,运行结果如下:
这里写图片描述
点击开始,波纹会上下起伏,点击停止,它会恢复到停止状态
接着,我们看下面的三个按钮是怎么实现的,其实是两个自定义的Button

CircleWaveButton.java 开始按钮

package com.adu.running.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.RadialGradient;import android.graphics.Shader;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.widget.Button;import com.adu.running.R;/** * Created by adu on 2016/10/30. */public class CircleWaveButton extends Button{    //这是默认颜色    private int paintColor= R.color.circle_bule_bbd4e7;    //画圆的画笔    private Paint paint = new Paint();    //画字的画笔    private Paint textPaint = new Paint();    private int radiusInt = 0;    //这是默认文字内容    private String text="开始";    private Boolean isStart = false;    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            invalidate();            if (isStart) {                radiusInt++;                if (radiusInt > 50) {                    radiusInt = 0;                }                sendEmptyMessageDelayed(0, 20);            }        }    };    public CircleWaveButton(Context context) {        super(context);        init();    }    public CircleWaveButton(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public CircleWaveButton(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        setBackgroundColor(getResources().getColor(R.color.running));    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int centre = getWidth() / 2;        int radius = centre;        //绘制圆的画笔        Paint newPaint = new Paint();        newPaint.setAntiAlias(true);        // 去除画笔锯齿        newPaint.setStyle(Paint.Style.FILL);// 设置风格为实线        //用来进行环形渲染        Shader shader = new RadialGradient(centre, centre, radius, getResources().getColor(paintColor), getResources().getColor(R.color.running), Shader.TileMode.CLAMP);        //设置图像效果,使用Shader可以绘制出各种渐变效果        newPaint.setShader(shader);        //绘制圆形(圆心的x坐标,圆心的y坐标,圆的半径,绘制时所使用的画笔)        canvas.drawCircle(centre, centre, radius, newPaint);        paint.setColor(getResources().getColor(paintColor));        paint.setStyle(Paint.Style.FILL);   //设置风格为实线        paint.setAntiAlias(true);           // 去除画笔锯齿        for(int i=1;i<=5;i++){            paint.setAlpha(20*i);//设置绘制图形的透明度            //绘制圆形(圆心的x坐标,圆心的y坐标,圆的半径,绘制时所使用的画笔)            canvas.drawCircle(centre, centre, radius * (10- i)/ 10, paint);        }        paint.setColor(getResources().getColor(paintColor));        paint.setStyle(Paint.Style.STROKE);     //设置风格为空心        paint.setStrokeWidth(radius * 2 / 12);  //设置线宽        for(int i=0;i<3;i++) {            paint.setAlpha(60-i*20);//设置绘制图形的透明度            //绘制圆形(圆心的x坐标,圆心的y坐标,圆的半径,绘制时所使用的画笔)            canvas.drawCircle(centre, centre, radius * (14-i*2 + radiusInt / 50.0f) / 16, paint);        }        textPaint.setTextSize(getTextSize());        textPaint.setAntiAlias(true);        textPaint.setStyle(Paint.Style.FILL);        textPaint.setColor(getTextColors().getDefaultColor());        //获取文字的宽度值        float length = textPaint.measureText(text);        //绘制文字        canvas.drawText(text, centre - length / 2, centre + getTextSize() / 3, textPaint);    }    public void start() {        isStart = true;        handler.sendEmptyMessage(0);    }    public void stop() {        isStart = false;        handler.removeMessages(0);    }}

我们在MainActivity设置CircleWaveButton的点击事件调用它的start( )方法,会看到下面的效果
这里写图片描述
这里写图片描述

下面我们看最后一个自定义Button,结束的Button和暂停的Button只是颜色不一样,其他都是一样的。

CircleButton.xml暂停结束按钮

package com.adu.running.view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.util.AttributeSet;import android.widget.Button;import com.adu.running.R;/** * Created by adu on 2016/10/30. */public class CircleButton extends Button {    //画圆    private Paint paint = new Paint();    //画字    private Paint textPaint = new Paint();    private int color;    public CircleButton(Context context) {        super(context);        init();    }    public CircleButton(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public CircleButton(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        setBackgroundColor(getResources().getColor(R.color.running));        color=getResources().getColor(R.color.running);    }    @Override protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        int centre = getWidth() / 2;        int radius = centre;        paint.setColor(color);        paint.setStyle(Paint.Style.FILL);        paint.setAntiAlias(true);        //绘制圆形(圆心的x坐标,圆心的y坐标,圆的半径,绘制时所使用的画笔)        canvas.drawCircle(centre, centre, radius, paint);        textPaint.setTextSize(getTextSize());        textPaint.setAntiAlias(true);        textPaint.setStyle(Paint.Style.FILL);        textPaint.setColor(getTextColors().getDefaultColor());        float length = textPaint.measureText(getText().toString());        //绘制文字        canvas.drawText(getText().toString(), centre - length / 2, centre + getTextSize() / 3, textPaint);    }    //改变文字颜色    public void setPaintColor(int color) {        this.color = getResources().getColor(color);    }}

我们就不在测试这个Button了,直接写开始的那个效果
点击开始按钮,然后隐藏它,一个动画的效果出现结束和暂停按钮,并且开启波纹,点暂停波纹停止,点击结束退出。
我们之间在MainActivity中写代码

package com.adu.running;import android.animation.Animator;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.content.Context;import android.graphics.Point;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.Display;import android.view.View;import android.view.WindowManager;import android.view.animation.DecelerateInterpolator;import butterknife.BindView;import butterknife.ButterKnife;import butterknife.OnClick;import com.adu.running.view.CircleButton;import com.adu.running.view.CircleWaveButton;import com.adu.running.view.WaveView;public class MainActivity extends AppCompatActivity {    @BindView(R.id.wave_view) WaveView waveView;    @BindView(R.id.startButton) CircleWaveButton startButton;   //开始    @BindView(R.id.stop) CircleButton stopButton;               //停止    @BindView(R.id.bt_continue) CircleButton btContinue;        //暂停继续    private boolean isPause = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        initView();    }    private void initView() {        btContinue.setPaintColor(R.color.text_color_1e78be);        startButton.setPaintColor(R.color.circle_bule_bbd4e7);        stopButton.setPaintColor(R.color.circle_red_cd3a33);        startButton.start();    }    @OnClick({ R.id.stop, R.id.startButton, R.id.bt_continue })    public void onClick(View view) {        switch (view.getId()) {            case R.id.stop:     //停止并退出                System.exit(0);                break;            case R.id.startButton:  //开始                startAnimation();                waveView.start();                break;            case R.id.bt_continue:  //暂停继续                if (isPause){                    btContinue.setText("暂停");                    isPause = false;                    waveView.start();                }else {                    btContinue.setText("继续");                    isPause = true;                    waveView.stop();                }                break;        }    }    /**     * 按钮动画效果实现的方法     */    private void startAnimation() {        btContinue.setVisibility(View.VISIBLE);//显示暂停按钮        stopButton.setVisibility(View.VISIBLE);//显示停止按钮        //获取屏幕的大小,并把屏幕的宽也就是x赋值给width        WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);        Display display = wm.getDefaultDisplay();        Point size = new Point();        display.getSize(size);        int width = size.x;        //属性动画,传入"alpha"参数,将开始按钮从不透明改变为透明        Animator animatorStart = ObjectAnimator.ofFloat(startButton, "alpha", 1.0f, 0f);        AnimatorSet animatorSetStart = new AnimatorSet();//组合动画        animatorSetStart.playTogether(animatorStart);        animatorSetStart.setInterpolator(new DecelerateInterpolator());//动画效果设置为减速        animatorSetStart.setDuration(1000);//设置动画时长        //将暂停按钮由透明改变为不透明        Animator animatorContinue1 = ObjectAnimator.ofFloat(btContinue, "alpha", 0f, 1.0f);        animatorContinue1.setDuration(3000);//设置动画时长        //将暂停按钮从中间向右边移动        Animator animatorContinue2 = ObjectAnimator.ofFloat(btContinue, "translationX", -width / 3, btContinue.getX());        animatorContinue2.setDuration(2000);//设置动画时长        AnimatorSet animatorSetContinue = new AnimatorSet();        animatorSetContinue.playTogether(animatorContinue1, animatorContinue2);// 并行        animatorSetContinue.setInterpolator(new DecelerateInterpolator());//动画效果设置为减速        //将结束按钮由透明改变为不透明        Animator animatorStop1 = ObjectAnimator.ofFloat(stopButton, "alpha", 0f, 1.0f);        animatorStop1.setDuration(3000);//设置动画时长        //将结束按钮从中间向左边移动        Animator animatorStop2 = ObjectAnimator.ofFloat(stopButton, "translationX", width / 3, 0);        animatorStop2.setDuration(2000);//设置动画时长        AnimatorSet animatorSetStop = new AnimatorSet();        animatorSetStop.playTogether(animatorStop1, animatorStop2);// 并行        animatorSetStop.setInterpolator(new DecelerateInterpolator());//动画效果设置为减速        //启动三个按钮动画        animatorSetStart.start();        animatorSetStop.start();        animatorSetContinue.start();        startButton.setVisibility(View.GONE);    }}

我们运行代码,看看效果
这里写图片描述

我们就简单的完成了一个这样的动画效果,看起来还是蛮酷的嘛。


这篇博客,我们主要学习了三个自定义View的实现,然后组合在一起形成一个很酷的效果。
下篇博客,我开始给大家讲这个动画和计步器连接起来开始计算步数的效果

我把这篇博客的Demo上传到这里,如果想直接看完整的动画和计步器的代码,请大家看我的GitHub,如果喜欢的话可以star一下,谢谢大家的支持


欢迎有兴趣的同学加我朋友的QQ群:点击直接加群555974449 我们一起来玩吧。

点击下载Demo

2 0
原创粉丝点击