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. Linux 高性能服务器编程——I/O复用的高级应用

    高级应用一:非阻塞connect connect系统调用的man手册中有如下的一段内容: EINPROGRESS The socket is non-blocking and the connecti ...

  2. Dynamics CRM2016 Web API之更新记录

    本篇继续探索web api,介绍如何通过web api更新记录. 下面是一段简单的更新代码,更新了几个不同类型的字段,entity的赋值和前篇创建时候的一样的. var entity = {}; en ...

  3. [ExtJS5学习笔记]第三十二节 sencha extjs 5与struts2的ajax交互配置

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/43487751 本文作者:sushengmiyan ------------------ ...

  4. Android开发学习之路--Notification之初体验

    一般当我们收到短信啊,微信啊,或者有些app的提醒,我们都会在通知栏收到一天简单的消息,然后点击消息进入到app里面,其实android中有专门的Notification的类可以完成这个工作,这里就实 ...

  5. JQuery实战---窗口效果

    在前面的相关博文中,小编对jquery的相关知识进行了简单的总结,关于jquery的很多小的知识点,都需要我们自己去动手和实践,一行行代码都需要我们自己亲自动手去敲,今天我们继续来学习jquery的相 ...

  6. 最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)

    ===================================================== 最简单的基于FFmpeg的封装格式处理系列文章列表: 最简单的基于FFmpeg的封装格式处理 ...

  7. MVPHelper更新日志 --- 新增常规分包模式

    MVPHelper是一款可以自动生成MVP接口以及实现类的android studio插件,彻底解放双手! MVPHelper更新版本啦. 由于之前只支持contract模式,不是很符合大众口味 所以 ...

  8. linq---我为你提笔序,你的美不只查询语句

    LinQ百度百科对她这样解释,是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据. LINQ是Language Int ...

  9. J2EE进阶(二)从零开始之Struts2

    J2EE进阶(二)从零开始之Struts2 以前自己总是听说什么SSH框架,不明觉厉.现在自己要重整旗鼓,开始系统性的学习SSH框架了.首先开始Struts2的学习.其实自己之前参与过Struts2项 ...

  10. JSP连接MySQL时出现--错误:Access denied for user 'root'@'localhost' (using password: YES)'解决方案

    用代码进行用户验证的时候总是出现这个错误,翻译一下,应该是root用户的是权限的问题没有放开. 那就想办法解决一下吧,具体的来说可以有这样的几种方式. 解决方法,首先想到的是先重启一下MySQL服务吧 ...