Android自定義WaveView實(shí)現(xiàn)波浪進(jìn)度效果
實(shí)現(xiàn)原理
首先就是自定義個(gè)WaveView 繼承View,然后再WaveView 內(nèi)部實(shí)現(xiàn)代碼邏輯:
① 水波就波嘛? sin函數(shù)? 貝塞爾曲線? 都行,這里就用二階貝塞 爾曲線去畫吧
② 波要?jiǎng)勇?,怎么?dòng)呢?線程? 好吧 這里用了個(gè)Handler。
③繪制波首先要找點(diǎn),那么在onMeasure()里找出需要的點(diǎn)咯,這里就暫時(shí)展示一個(gè)波段吧,一個(gè)波長(zhǎng)移動(dòng)左邊不就沒了?OK 那就兩個(gè)波吧,吼吼,兩個(gè)波(猥瑣男潛質(zhì)表露無遺?。?。接下來就是Handler 結(jié)合 onDraw()繪制。OK,那就先看我Word繪制的粗癟的波動(dòng)圖,請(qǐng)看VCR,oh,no... gif

意思就是波平移一個(gè)波長(zhǎng)之后回到初始位置繼續(xù)平移循環(huán)。
好吧,有人說了,這么簡(jiǎn)單的邏輯你要啰嗦那么多???
好吧,我承認(rèn),我有唐僧的潛質(zhì)。。。
閑話就不說了,先上
效果圖

示例代碼如下
調(diào)用的Activity
* Created by LiuDong on 2016/12/22.
* Email:15002102128@126.com
*/
public class WaveActivity extends Activity {
LD_WaveView waveView;//方形
LD_WaveView waveCircleView;//圓形
private int progrees=0;//進(jìn)度
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
if (progrees==100) progrees=0;
Log.i("progress",progrees+"");
waveView.setmProgress(progrees++);
waveCircleView.setmProgress(progrees++);
mHandler.sendEmptyMessageDelayed(0,100);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wave);
waveView= (LD_WaveView) findViewById(R.id.waveView);
waveCircleView= (LD_WaveView) findViewById(R.id.waveViewCircle);
mHandler.sendEmptyMessageDelayed(0,10);
}
}
xml布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:background="@color/ld_White" android:layout_height="match_parent"> <com.dadong.ld_tools.widget.LD_WaveView android:id="@+id/waveViewCircle" android:layout_marginTop="20dp" android:layout_width="100dp" android:layout_centerHorizontal="true" android:layout_height="100dp" app:wave_color="@color/ld_Black" app:wave_circle="true" /> <com.dadong.ld_tools.widget.LD_WaveView android:id="@+id/waveView" android:layout_width="100dp" android:layout_height="100dp" app:wave_color="@color/ld_Black" app:wave_circle="false" android:layout_centerInParent="true" /> </RelativeLayout>
自定義WaveView
/**
* Created by LiuDong on 2016/12/23.
* Email:15002102128@126.com
*/
public class LD_WaveView extends View {
private int mProgress;//進(jìn)度
private int mTimeStep = 10;//時(shí)間間隔
private int mSpeed = 5;//波單次移動(dòng)的距離
private int mViewHeight;//視圖寬高
private int mViewWidth;//視圖寬度
private int mLevelLine;// 基準(zhǔn)線
private int mWaveLength;//波長(zhǎng) 暫定view寬度為一個(gè)波長(zhǎng)
private int mStrokeWidth;//園的線寬
private RectF rectF;//圓環(huán)區(qū)域
private int mWaveHeight;//波峰高度
private int mLeftWaveMoveLength;//波平移的距離,用來控制波的起點(diǎn)位置
private int mWaveColor;//波的顏色
private Paint mPaint;//畫筆
private Paint mCirclePaint;//圓環(huán)畫筆
private Paint mBorderPaint;//邊界畫筆
private int mBorderWidth=4;//邊界寬度
private Paint mTextPaint;//文字畫筆
private Path mPath;//繪畫線
private List<Point> mPoints;//點(diǎn)的集合
private boolean isMeasure = false;//是否已測(cè)量過
private boolean isCircle=false;//是否圓形默認(rèn)false,可屬性代碼設(shè)置
//處理消息
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
initWaveMove();
}
};
/**
* 初始化波的移動(dòng)
*/
private void initWaveMove(){
mLeftWaveMoveLength+=mSpeed;//波向右移動(dòng)距離增加mSpeed;
if (mLeftWaveMoveLength>=mWaveLength){//當(dāng)增加到一個(gè)波長(zhǎng)時(shí)回復(fù)到0
mLeftWaveMoveLength=0;
}
invalidate();
}
public LD_WaveView(Context context) {
this(context, null);
}
public LD_WaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LD_WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getAttr(context, attrs, defStyleAttr);
init();
}
/**
* 初始化畫筆
*/
private void init() {
mPoints = new ArrayList<Point>();
//波浪軌跡畫筆
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(mWaveColor);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPath = new Path();
//文字畫筆
mTextPaint=new Paint();
mTextPaint.setColor(Color.RED);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTextSize(48);
//圓環(huán)畫筆
mCirclePaint=new Paint();
mCirclePaint.setAntiAlias(true);
mCirclePaint.setColor(Color.WHITE);
mCirclePaint.setStyle(Paint.Style.STROKE);
//邊界線畫筆
mBorderPaint=new Paint();
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mWaveColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setStyle(Paint.Style.STROKE);
}
/**
* 獲取自定義的屬性值
*
* @param attrs
*/
private void getAttr(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LD_WaveView, defStyle, 0);
mWaveColor = a.getColor(R.styleable.LD_WaveView_wave_color, Color.RED);
isCircle=a.getBoolean(R.styleable.LD_WaveView_wave_circle,false);
a.recycle();
}
/**
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!isMeasure&&Math.abs(getMeasuredHeight()-getMeasuredWidth())<50) {//只計(jì)算一次就夠了 ,relativelayout的時(shí)候要繪制兩次 加個(gè)寬高判斷
mViewHeight = getMeasuredHeight();
mViewWidth = getMeasuredWidth();
mLevelLine = mViewHeight; //初始化波的準(zhǔn)位線 起始位視圖最底部
{
mLevelLine = mViewHeight * (100-mProgress) / 100;
if (mLevelLine < 0) mLevelLine = 0;
}
//計(jì)算波峰值
mWaveHeight = mViewHeight / 20;//波峰暫定為view高度的1/20,如果需要設(shè)置 可設(shè)置set方法賦值;
mWaveLength = getMeasuredWidth();
//計(jì)算所有的點(diǎn) 這里取寬度為整個(gè)波長(zhǎng) 往左再延伸一個(gè)波長(zhǎng) 兩個(gè)波長(zhǎng)則需要9個(gè)點(diǎn)
for (int i = 0; i < 9; i++) {
int y = 0;
switch (i % 4) {
case 0:
y = mViewHeight;
break;
case 1:
y =mViewHeight+ mWaveHeight;
break;
case 2:
y = mViewHeight;
break;
case 3:
y = mViewHeight-mWaveHeight;
break;
}
Point point = new Point(-mWaveLength + i * mWaveLength / 4, y);
mPoints.add(point);
}
/**
* 計(jì)算圓環(huán)寬度
*/
int mIncircleRadius=mViewHeight<mViewWidth?mViewHeight/2:mViewWidth/2;//內(nèi)切圓半徑
int mcircumcircleRadius= (int) (Math.sqrt((float)(Math.pow(mViewHeight/2,2)+Math.pow(mViewWidth/2,2)))+0.5);//外接圓半徑
int radius=mcircumcircleRadius/2+mIncircleRadius/2;
rectF=new RectF(mViewWidth/2-radius,mViewHeight/2-radius,mViewWidth/2+radius,mViewHeight/2+radius);
mStrokeWidth=mcircumcircleRadius-mIncircleRadius;
mCirclePaint.setStrokeWidth(mStrokeWidth);//線是有寬度的 采用了這種方式畫圓環(huán)
isMeasure = true;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 繪制線條
*/
mPath.reset();
int i = 0;
mPath.moveTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mPoints.get(0).getY()-mViewHeight*mProgress/100);
for (; i < mPoints.size() - 2; i += 2) {
mPath.quadTo(mPoints.get(i + 1).getX()+mLeftWaveMoveLength, mPoints.get(i + 1).getY()-mViewHeight*mProgress/100, mPoints.get(i + 2).getX()+mLeftWaveMoveLength, mPoints.get(i + 2).getY()-mViewHeight*mProgress/100);
}
mPath.lineTo(mPoints.get(i).getX()+mLeftWaveMoveLength, mViewHeight);
mPath.lineTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mViewHeight);
mPath.close();
/**
* 繪制軌跡
*/
canvas.drawPath(mPath,mPaint);
Rect rect = new Rect();
String progress=String.format("%d%%",mProgress);
mTextPaint.getTextBounds(progress,0,progress.length(), rect);
int textHeight = rect.height();
if (mProgress>=50)//如果進(jìn)度達(dá)到50 顏色變?yōu)榘咨?,沒辦法啊,進(jìn)度在中間 不變顏色看不到
mTextPaint.setColor(Color.WHITE);
else
mTextPaint.setColor(mWaveColor);
canvas.drawText(progress,0,progress.length(),mViewWidth/2,mViewHeight/2+textHeight/2,mTextPaint);
if (isCircle) {
/**
* 繪制圓環(huán)
*/
canvas.drawArc(rectF, 0, 360, true, mCirclePaint);
Paint circlePaint = new Paint();
circlePaint.setStrokeWidth(5);
circlePaint.setColor(Color.WHITE);
circlePaint.setAntiAlias(true);
circlePaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(mViewWidth / 2, mViewHeight / 2, mViewHeight / 2, circlePaint);
/**
* 繪制邊界
*/
mBorderPaint.setStrokeWidth(mBorderWidth/2);
canvas.drawCircle(mViewWidth/2,mViewHeight/2,mViewHeight/2-mBorderWidth/2,mBorderPaint);
}else {
/**
* 繪制矩形邊框
*/
canvas.drawRect(0,0,mViewWidth,mViewHeight,mBorderPaint);
}
//
handler.sendEmptyMessageDelayed(0,mTimeStep);
}
/**
* 設(shè)置進(jìn)度 基準(zhǔn)線
* @param mProgress
*/
public void setmProgress(int mProgress) {
this.mProgress = mProgress;
mLevelLine=(100-mProgress)*mViewHeight/100;
}
/**
* 設(shè)置是否為圓形
* @param circle
*/
public void setCircle(boolean circle) {
isCircle = circle;
}
}
自定義屬性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="LD_WaveView"> <attr name="wave_color" format="color"></attr> <attr name="wave_circle" format="boolean"></attr> </declare-styleable> </resources>
總結(jié)
好了,以上就是這篇文章的全部?jī)?nèi)容了,代碼里備注應(yīng)該還算比較清楚了,希望能對(duì)一些人有一些幫助,瑕疵不足之處歡迎指正,或者有好的建議。也可以留言交流。
相關(guān)文章
sweet alert dialog 在android studio應(yīng)用問題說明詳解
這篇文章主要介紹了sweet alert dialog 在android studio應(yīng)用問題說明詳解的相關(guān)資料,本文圖文并茂介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
Android編程之繪制文本(FontMetrics)實(shí)現(xiàn)方法
這篇文章主要介紹了Android編程之繪制文本(FontMetrics)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android使用FontMetrics對(duì)象繪制文本的相關(guān)技巧,需要的朋友可以參考下2015-12-12
Android實(shí)現(xiàn)雙層ViewPager嵌套
這篇文章主要介紹了Android實(shí)現(xiàn)雙層ViewPager嵌套,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04
Android利用Sensor(傳感器)實(shí)現(xiàn)水平儀功能
這篇文章主要為大家詳細(xì)介紹了Android利用Sensor傳感器實(shí)現(xiàn)水平儀功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Android多媒體應(yīng)用使用MediaPlayer播放音頻
這篇文章主要為大家詳細(xì)介紹了Android多媒體應(yīng)用使用MediaPlayer播放音頻,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android自定義view實(shí)現(xiàn)有header和footer作為layout使用的滾動(dòng)控件
這篇文章主要介紹了Android自定義view實(shí)現(xiàn)有header和footer的滾動(dòng)控件,可以在XML中當(dāng)Layout使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-11-11
Jetpack Compose實(shí)現(xiàn)列表和動(dòng)畫效果詳解
這篇文章主要為大家詳細(xì)講講Jetpack Compose實(shí)現(xiàn)列表和動(dòng)畫效果的方法步驟,文中的代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-06-06

