首页 技术 正文
技术 2022年11月20日
0 收藏 770 点赞 5,013 浏览 11828 个字

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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,114
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,586
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,432
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,203
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,838
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,923