Android仿水波紋流量球進(jìn)度條控制器
仿水波紋流球進(jìn)度條控制器,Android實(shí)現(xiàn)高端大氣的主流特效,供大家參考,具體內(nèi)容如下
效果圖:
CircleView
這里主要是實(shí)現(xiàn)中心圓以及水波特效
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; // 繪制文字顯示在圓形中間,只是我沒有設(shè)置,我覺得寫在布局上也挺好的 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) { // 不斷發(fā)消息給自己,使自己不斷被重繪 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)); // 計(jì)算當(dāng)前油量線和水平中線的距離 float centerOffset = Math.abs(mScreenWidth / 2 * mWaterLevel - mScreenWidth / 4); // 計(jì)算油量線和與水平中線的角度 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); // 如果未開始(未調(diào)用startWave方法),繪制一個(gè)扇形 if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) { // 繪制,即水面靜止時(shí)的高度 RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4, mScreenWidth * 3 / 4, mScreenHeight * 3 / 4); canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint); return; } // 繪制,即水面靜止時(shí)的高度 // 繪制,即水面靜止時(shí)的高度 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時(shí)c都會(huì)自增 c = (1L + c); float f1 = mScreenHeight * (1.0F - (0.25F + mWaterLevel / 2)) - mAmplitude; // 當(dāng)前油量線的長度 float waveWidth = (float) Math.sqrt(mScreenWidth * mScreenWidth / 16 - centerOffset * centerOffset); // 與圓半徑的偏移量 float offsetWidth = mScreenWidth / 4 - waveWidth; int top = (int) (f1 + mAmplitude); mPath.reset(); // 起始振動(dòng)X坐標(biāo),結(jié)束振動(dòng)X坐標(biāo) 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(); // 關(guān)閉硬件加速,防止異常unsupported operation exception this.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); } /** * @category 開始波動(dòng) */ public void startWave() { if (!mStarted) { this.c = 0L; mStarted = true; this.mHandler.sendEmptyMessage(0); } } /** * @category 停止波動(dòng) */ public void stopWave() { if (mStarted) { this.c = 0L; mStarted = false; this.mHandler.removeMessages(0); } } /** * @category 保存狀態(tài) */ 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]; } }; } }
我們運(yùn)行一下
其實(shí)他是十分的空曠的,所以也值得我們?nèi)ザㄖ?,我們在中間加個(gè)流量顯示,再加個(gè)進(jìn)度條
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>
我們要實(shí)現(xiàn)這個(gè),就要調(diào)用它的初始化以及start方法
mCircleView = (CircleView) findViewById(R.id.wave_view); // 設(shè)置多高,float,0.1-1F mCircleView.setmWaterLevel(0.1F); // 開始執(zhí)行 mCircleView.startWave(); 別忘了activity銷毀的時(shí)候把它回收哦 @Override protected void onDestroy() { // TODO Auto-generated method stub mCircleView.stopWave(); mCircleView = null; super.onDestroy(); }
我們再運(yùn)行一遍
但是我們要怎么讓水波紋隨著進(jìn)度條一起上升下降尼?,這里我們就要用到我們剛才寫的SeekBar了,我們實(shí)現(xiàn)它的
setOnSeekBarChangeListener來監(jiān)聽,這樣我們就要復(fù)寫他的三個(gè)方法,這里我們只要用到一個(gè) public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //跟隨進(jìn)度條滾動(dòng) mCircleView.setmWaterLevel((float) progress / 100); }
這里,我們要這樣算的,我們設(shè)置高度的單位是float,也就是從0-1F,而我們的進(jìn)度是int progress,從0-100,我們就要用(float) progress / 100)并且強(qiáng)轉(zhuǎn)來得到單位,好了,我們現(xiàn)在水波紋的高度就是隨著我們的進(jìn)度條一起變化了,我們再來運(yùn)行一下
好的,這樣的話,我們就只剩下一個(gè)了,就是讓大小隨著我們的進(jìn)度條變化了,這里我們因?yàn)楦耈I不能再主線程中操作,所以我們需要用到我們的老伙計(jì)Handler了,但是用到handler還不夠,我們的進(jìn)度條數(shù)值也是在內(nèi)部類里面,所以這里我們需要用到Handler來傳值了,這里我們用的是Bundle,我們還是在onProgressChanged方法中操作了
//創(chuàng)建一個(gè)消息 Message message = new Message(); Bundle bundle = new Bundle(); //put一個(gè)int值 bundle.putInt("progress", progress); //裝載 message.setData(bundle); //發(fā)送消息 handler.sendMessage(message); //創(chuàng)建表示 message.what = 1;
消息發(fā)送過去了,我們就在前面寫個(gè)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"); } } };
這里的計(jì)算公式尼,是當(dāng)前的數(shù)值/100得到百分比再去*最大值。我們現(xiàn)在可以完整的運(yùn)行一下了,其實(shí)和最上面運(yùn)行的圖片是一樣的
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); // 設(shè)置多高,float,0.1-1F mCircleView.setmWaterLevel(0.1F); // 開始執(zhí)行 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); // 創(chuàng)建一個(gè)消息 Message message = new Message(); Bundle bundle = new Bundle(); // put一個(gè)int值 bundle.putInt("progress", progress); // 裝載 message.setData(bundle); // 發(fā)送消息 handler.sendMessage(message); // 創(chuàng)建表示 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(); } }
以上就是本文的全部內(nèi)容,希望對大家學(xué)習(xí)Android軟件編程有所幫助。
相關(guān)文章
android實(shí)現(xiàn)系統(tǒng)信息推送
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)系統(tǒng)信息推送,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Android獲取超級管理員權(quán)限的實(shí)現(xiàn)
這篇文章主要介紹了Android獲取超級管理員權(quán)限的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個(gè)Android項(xiàng)目(eclipse+android sdk)
這篇文章主要介紹了用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個(gè)Android項(xiàng)目,需要的朋友可以參考下2015-09-09Android開發(fā)實(shí)現(xiàn)多進(jìn)程彈窗效果
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)實(shí)現(xiàn)多進(jìn)程彈窗效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Android使用fastjson庫解析json字符串實(shí)戰(zhàn)
fastjson是一個(gè)Java語言編寫的高性能功能完善的JSON庫,它采用一種“假定有序快速匹配”的算法,把JSON?Parse的性能提升到極致,是目前Java語言中最快的JSON庫,Fastjson接口簡單易用,已經(jīng)被廣泛使用在緩存序列化、協(xié)議交互、Web輸出、Android客戶端等多種應(yīng)用場景2023-11-11Convert WebP to PNG using java
本文主要介紹Convert WebP to PNG using java,這里對 WebP 做了詳細(xì)說明,并講解Linux 環(huán)境下WebP 轉(zhuǎn)png格式的示例,有興趣的小伙伴可以參考下2016-08-08Android Studio與SVN版本控制程序的協(xié)作使用指南
這篇文章主要介紹了Android Studio與SVN版本控制程序的協(xié)作使用指南,使用Gradle插件自動(dòng)填寫SVN號并發(fā)布到指定目錄的方法,需要的朋友可以參考下2016-03-03android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider
這篇文章主要介紹了android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider,有興趣的可以了解一下。2016-11-11