Android特效专辑(十一)——仿水波纹流球进度条控制器,实现高端大气的主流特效


今天看到一个效果挺不错的,就模仿了下来,加上了一些自己想要的效果,感觉还不错的样子,所以就分享出来了,话不多说,上图

截图

CircleView

这里主要是实现中心圆以及水波特效

package com.lgl.circleview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ProgressBar;

/**
 * 水波圆
 *
 * @author lgl
 *
 */
public class CircleView extends View {

    private Context mContext;

    private int mScreenWidth;
    private int mScreenHeight;

    private Paint mRingPaint;
    private Paint mCirclePaint;
    private Paint mWavePaint;
    private Paint linePaint;
    private Paint flowPaint;
    private Paint leftPaint;

    private int mRingSTROKEWidth = 15;
    private int mCircleSTROKEWidth = 2;
    private int mLineSTROKEWidth = 1;

    private int mCircleColor = Color.WHITE;
    private int mRingColor = Color.WHITE;
    private int mWaveColor = Color.WHITE;

    private Handler mHandler;
    private long c = 0L;
    private boolean mStarted = false;
    private final float f = 0.033F;
    private int mAlpha = 50;// 透明度
    private float mAmplitude = 10.0F; // 振幅
    private float mWaterLevel = 0.5F;// 水高(0~1)
    private Path mPath;

    // 绘制文字显示在圆形中间,只是我没有设置,我觉得写在布局上也挺好的
    private String flowNum = "";
    private String flowLeft = "还剩余";

    /**
     * @param context
     */
    public CircleView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        mContext = context;
        init(mContext);
    }

    /**
     * @param context
     * @param attrs
     */
    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        mContext = context;
        init(mContext);
    }

    /**
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // TODO Auto-generated constructor stub
        mContext = context;
        init(mContext);
    }

    public void setmWaterLevel(float mWaterLevel) {
        this.mWaterLevel = mWaterLevel;
    }

    private void init(Context context) {
        mRingPaint = new Paint();
        mRingPaint.setColor(mRingColor);
        mRingPaint.setAlpha(50);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setAntiAlias(true);
        mRingPaint.setStrokeWidth(mRingSTROKEWidth);

        mCirclePaint = new Paint();
        mCirclePaint.setColor(mCircleColor);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setStrokeWidth(mCircleSTROKEWidth);

        linePaint = new Paint();
        linePaint.setColor(mCircleColor);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(mLineSTROKEWidth);

        flowPaint = new Paint();
        flowPaint.setColor(mCircleColor);
        flowPaint.setStyle(Paint.Style.FILL);
        flowPaint.setAntiAlias(true);
        flowPaint.setTextSize(36);

        leftPaint = new Paint();
        leftPaint.setColor(mCircleColor);
        leftPaint.setStyle(Paint.Style.FILL);
        leftPaint.setAntiAlias(true);
        leftPaint.setTextSize(36);

        mWavePaint = new Paint();
        mWavePaint.setStrokeWidth(1.0F);
        mWavePaint.setColor(mWaveColor);
        mWavePaint.setAlpha(mAlpha);
        mPath = new Path();

        mHandler = new Handler() {
            @Override
            public void handleMessage(android.os.Message msg) {
                if (msg.what == 0) {
                    invalidate();
                    if (mStarted) {
                        // 不断发消息给自己,使自己不断被重绘
                        mHandler.sendEmptyMessageDelayed(0, 60L);
                    }
                }
            }
        };
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = measure(widthMeasureSpec, true);
        int height = measure(heightMeasureSpec, false);
        if (width < height) {
            setMeasuredDimension(width, width);
        } else {
            setMeasuredDimension(height, height);
        }

    }

    /**
     * @category 测量
     * @param measureSpec
     * @param isWidth
     * @return
     */
    private int measure(int measureSpec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        int padding = isWidth ? getPaddingLeft() + getPaddingRight()
                : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = isWidth ? getSuggestedMinimumWidth()
                    : getSuggestedMinimumHeight();
            result += padding;
            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
                    result = Math.max(result, size);
                } else {
                    result = Math.min(result, size);
                }
            }
        }
        return result;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
        mScreenWidth = w;
        mScreenHeight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        // 得到控件的宽高
        int width = getWidth();
        int height = getHeight();
        setBackgroundColor(mContext.getResources().getColor(R.color.main_bg));
        // 计算当前油量线和水平中线的距离
        float centerOffset = Math.abs(mScreenWidth / 2 * mWaterLevel
                - mScreenWidth / 4);
        // 计算油量线和与水平中线的角度
        float horiAngle = (float) (Math.asin(centerOffset / (mScreenWidth / 4)) * 180 / Math.PI);
        // 扇形的起始角度和扫过角度
        float startAngle, sweepAngle;
        if (mWaterLevel > 0.5F) {
            startAngle = 360F - horiAngle;
            sweepAngle = 180F + 2 * horiAngle;
        } else {
            startAngle = horiAngle;
            sweepAngle = 180F - 2 * horiAngle;
        }

        canvas.drawLine(mScreenWidth * 3 / 8, mScreenHeight * 5 / 8,
                mScreenWidth * 5 / 8, mScreenHeight * 5 / 8, linePaint);

        float num = flowPaint.measureText(flowNum);
        canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2,
                mScreenHeight * 4 / 8, flowPaint);
        float left = leftPaint.measureText(flowLeft);
        canvas.drawText(flowLeft, mScreenWidth * 4 / 8 - left / 2,
                mScreenHeight * 3 / 8, leftPaint);

        // 如果未开始(未调用startWave方法),绘制一个扇形
        if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {
            // 绘制,即水面静止时的高度
            RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4,
                    mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);
            canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);
            return;
        }
        // 绘制,即水面静止时的高度
        // 绘制,即水面静止时的高度
        RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4,
                mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);
        canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);

        if (this.c >= 8388607L) {
            this.c = 0L;
        }
        // 每次onDraw时c都会自增
        c = (1L + c);
        float f1 = mScreenHeight * (1.0F - (0.25F + mWaterLevel / 2))
                - mAmplitude;
        // 当前油量线的长度
        float waveWidth = (float) Math.sqrt(mScreenWidth * mScreenWidth / 16
                - centerOffset * centerOffset);
        // 与圆半径的偏移量
        float offsetWidth = mScreenWidth / 4 - waveWidth;

        int top = (int) (f1 + mAmplitude);
        mPath.reset();
        // 起始振动X坐标,结束振动X坐标
        int startX, endX;
        if (mWaterLevel > 0.50F) {
            startX = (int) (mScreenWidth / 4 + offsetWidth);
            endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth);
        } else {
            startX = (int) (mScreenWidth / 4 + offsetWidth - mAmplitude);
            endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth + mAmplitude);
        }
        // 波浪效果
        while (startX < endX) {
            int startY = (int) (f1 - mAmplitude
                    * Math.sin(Math.PI
                            * (2.0F * (startX + this.c * width * this.f))
                            / width));
            canvas.drawLine(startX, startY, startX, top, mWavePaint);
            startX++;
        }
        canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4
                + mRingSTROKEWidth / 2, mRingPaint);

        canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2,
                mScreenWidth / 4, mCirclePaint);
        canvas.restore();
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.progress = (int) c;
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        c = ss.progress;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // 关闭硬件加速,防止异常unsupported operation exception
        this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }

    /**
     * @category 开始波动
     */
    public void startWave() {
        if (!mStarted) {
            this.c = 0L;
            mStarted = true;
            this.mHandler.sendEmptyMessage(0);
        }
    }

    /**
     * @category 停止波动
     */
    public void stopWave() {
        if (mStarted) {
            this.c = 0L;
            mStarted = false;
            this.mHandler.removeMessages(0);
        }
    }

    /**
     * @category 保存状态
     */
    static class SavedState extends BaseSavedState {
        int progress;

        /**
         * Constructor called from {@link ProgressBar#onSaveInstanceState()}
         */
        SavedState(Parcelable superState) {
            super(superState);
        }

        /**
         * Constructor called from {@link #CREATOR}
         */
        private SavedState(Parcel in) {
            super(in);
            progress = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(progress);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

}

我们运行一下

其实他是十分的空旷的,所以也值得我们去定制,我们在中间加个流量显示,再加个进度条

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:background="@color/main_bg" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:text="流量"
        android:textColor="@android:color/white"
        android:textSize="18sp" />

    <com.lgl.circleview.CircleView
        android:id="@+id/wave_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_centerInParent="true" />

    <TextView
        android:id="@+id/power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textColor="@android:color/white" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="150dp" />

</RelativeLayout>

我们要实现这个,就要调用它的初始化以及start方法

    mCircleView = (CircleView) findViewById(R.id.wave_view);
        // 设置多高,float,0.1-1F
        mCircleView.setmWaterLevel(0.1F);
        // 开始执行
        mCircleView.startWave();

别忘了activity销毁的时候把它回收哦

@Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        mCircleView.stopWave();
        mCircleView = null;
        super.onDestroy();
    }

我们再运行一遍

但是我们要怎么让水波纹随着进度条一起上升下降尼?,这里我们就要用到我们刚才写的SeekBar了,我们实现它的setOnSeekBarChangeListener来监听,这样我们就要复写他的三个方法,这里我们只要用到一个

public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                //跟随进度条滚动
                mCircleView.setmWaterLevel((float) progress / 100);
                }

这里,我们要这样算的,我们设置高度的单位是float,也就是从0-1F,而我们的进度是int progress,从0-100,我们就要用(float) progress / 100)并且强转来得到单位,好了,我们现在水波纹的高度就是随着我们的进度条一起变化了,我们再来运行一下

好的,这样的话,我们就只剩下一个了,就是让大小随着我们的进度条变化了,这里我们因为更新UI不能再主线程中操作,所以我们需要用到我们的老伙计Handler了,但是用到handler还不够,我们的进度条数值也是在内部类里面,所以这里我们需要用到Handler来传值了,这里我们用的是Bundle,我们还是在onProgressChanged方法中操作了

                //创建一个消息
                Message message = new Message();
                Bundle bundle = new Bundle();
                //put一个int值
                bundle.putInt("progress", progress);
                //装载
                message.setData(bundle);
                //发送消息
                handler.sendMessage(message);
                //创建表示
                message.what = 1;

消息发送过去了,我们就在前面写个Handler去接收就是了


    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (msg.what == 1) {
                int num = msg.getData().getInt("progress");
                Log.i("num", num + "");
                power.setText((float) num / 100 * max + "M/" + max + "M");
            }
        }
    };

这里的计算公式尼,是当前的数值/100得到百分比再去*最大值。我们现在可以完整的运行一下了,其实和最上面运行的图片是一样的

MainActivity

package com.lgl.circleview;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends Activity {

    private CircleView mCircleView;
    private SeekBar mSeekBar;
    private TextView power;
    private int max = 1024;
    private int min = 102;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            if (msg.what == 1) {
                int num = msg.getData().getInt("progress");
                Log.i("num", num + "");
                power.setText((float) num / 100 * max + "M/" + max + "M");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActionBar().hide();
        setContentView(R.layout.activity_main);

        power = (TextView) findViewById(R.id.power);
        power.setText(min + "M/" + max + "M");

        mCircleView = (CircleView) findViewById(R.id.wave_view);
        // 设置多高,float,0.1-1F
        mCircleView.setmWaterLevel(0.1F);
        // 开始执行
        mCircleView.startWave();

        mSeekBar = (SeekBar) findViewById(R.id.seekBar);
        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                mCircleView.setmWaterLevel((float) progress / 100);
                // 创建一个消息
                Message message = new Message();
                Bundle bundle = new Bundle();
                // put一个int值
                bundle.putInt("progress", progress);
                // 装载
                message.setData(bundle);
                // 发送消息
                handler.sendMessage(message);
                // 创建表示
                message.what = 1;
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        mCircleView.stopWave();
        mCircleView = null;
        super.onDestroy();
    }
}

Demo下载地址:http://download.csdn.net/detail/qq_26787115/9435934

Android特效专辑(十一)——仿水波纹流量球进度条控制器,实现高端大气的主流特效的更多相关文章

  1. Android -- 真正的 高仿微信 打开网页的进度条效果

    (本博客为原创,http://www.cnblogs.com/linguanh/) 目录: 一,为什么说是真正的高仿? 二,为什么要搞缓慢效果? 三,我的实现思路 四,代码,内含注释 五,使用方法与截 ...

  2. Android中通过线程实现更新ProgressDialog(对话进度条)

    作为开发者我们需要经常站在用户角度考虑问题,比如在应用商城下载软件时,当用户点击下载按钮,则会有下载进度提示页面出现,现在我们通过线程休眠的方式模拟下载进度更新的演示,如图(这里为了截图方便设置对话进 ...

  3. Android UI系列-----时间、日期、Toasts和进度条Dialog

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  4. 【转】Android UI系列-----时间、日期、Toasts和进度条Dialog

    原文网址:http://www.cnblogs.com/xiaoluo501395377/p/3421727.html 您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注 ...

  5. Android简易实战教程--第二话《两种进度条》

    点击按钮模拟进度条下载进度,"下载"完成进度条消失. 代码如下: xml: <?xml version="1.0" encoding="utf- ...

  6. 阅读《Android 从入门到精通》(17)——进度条

    进度条(ProgressBar) java.lang.Object; android.view.View; android.widget.ProgressBar; ProgressBar 类方法 Pr ...

  7. 仿MIUI音量变化环形进度条实现

    Android中使用环形进度条的业务场景事实上蛮多的,比方下载文件的时候使用环形进度条.会给用户眼前一亮的感觉:再比方我大爱的MIUI系统,它的音量进度条就是使用环形进度条,尽显小米"为发烧 ...

  8. Android 自定义View—清爽小巧灵活的多节点进度条

    前言 最近项目有一个节点进度条的小需求,完成后,想分享出来希望可以帮到有需要的同学. 真机效果图 自定义View完整代码 开箱即用~,注释已经炒鸡详细了 /** * @description: 节点进 ...

  9. Android 自定义 View 圆形进度条总结

    Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...

随机推荐

  1. android 网络工具 之Android-Volley的demo

    1.今天详细的研究了Volley的使用,下面来给大家介绍一下: Android Volley 是Google开发的一个网络lib,可以让你更加简单并且快速的访问网络数据.Volley库的网络请求都是异 ...

  2. [安全]Back_Track_5 vm 版安装和使用

    下载安装 下载使用国内的镜像  http://mirrors.ustc.edu.cn/kali-images/kali-1.0.9/ 我这里是vm9.0 下载之后解压,然后打开vm,然后 文件--&g ...

  3. Servlet规范总结

    Servlet接口 Servlet规范的核心接口即是Servlet接口,它是所有Servlet类必须实现的接口,在Java Servelt API中已经提供了两个抽象类方便开发者实现Servlet类, ...

  4. J2EE规范标准

    J2EE是一个很大的平台体系,提供了很多服务.程序接口.协议等.这么庞大的体系必须要由一系列的标准进行规范,不然将会一片混乱.通过这些规范好的接口来开发程序将会使程序更加强壮.更加有生命力.总的来说, ...

  5. x264源代码简单分析:熵编码(Entropy Encoding)部分

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  6. UNIX网络编程——epoll 的accept , read, write(重要)

    在一个非阻塞的socket上调用read/write函数,返回EAGAIN或者EWOULDBLOCK(注:EAGAIN就是EWOULDBLOCK). 从字面上看,意思是: EAGAIN: 再试一次 E ...

  7. 1068. Find More Coins (30)

    题目如下: Eva loves to collect coins from all over the universe, including some other planets like Mars. ...

  8. Oracle Apps DBA 常用命令

    数据库启动监听 addlnctl.sh start instance 启动数据库 addbctl.sh start 启动应用服务器 adstrtal.sh 停止应用服务器 adstpall.sh -- ...

  9. FreeMarker生成word的代码

     用于生成word用的freemarker工具类 package com.ucap.netcheck.utils; import java.io.File; import java.io.File ...

  10. 【UNIX网络编程第三版】阅读笔记(一):代码环境搭建

    粗略的阅读过<TCP/IP详解>和<计算机网络(第五版)>后,开始啃这本<UNIX网络编程卷一:套接字联网API>,目前linux下的编程不算太了解,在阅读的过程中 ...