自定义控件—— 波浪形状圆形进度加载
时间管理的基础是精力管理,精力的高低、正负分影响到我们的效率
而时间是无法管理的,能够管理的只有自己,透过管理自己的习惯,管理自己的事件来达成对时间的管理。
而在每一天中,人生不丰于做多少事,而在于把重要的事情专注做、用心做,把它做到极致。
1、效果简阅
2、实现思路设计
3、初始化操作
private void init(Context context) {
//绘制圆形的 Paint
mCicrlPaint = new Paint();
mCicrlPaint.setAntiAlias(true);
mCicrlPaint.setColor(Color.BLUE);
//绘制进度的 Paint
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setColor(Color.RED);
//设置只绘制重叠的部分
mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//绘制文字的 Paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTextSize(20);
//绘制进度的Path
mPath = new Path();
/**
* 手势识别监听
*/
final GestureDetector gestureDetector = new GestureDetector(listener);
/**
* 将触摸识别事件传递给 GestureDetector
*/
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
this.setClickable(true);
//设置布局加载监听
this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//更新控件的大小
viewHeight = ProgressView.this.getHeight();
viewWidth = ProgressView.this.getWidth();
//创建 bitmap 与 canvas
bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(bitmap);
/**
* 更新默认设置
*/
updateDefaulBuildValue();
}
});
}
GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
/**
* 单击点击监听
* @param e
* @return
*/
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
System.out.println("sing click ");
if (clickListener != null) {
clickListener.onSingleClick(e);
return true;
}
return super.onSingleTapConfirmed(e);
}
/**
* 双击点击监听
* @param e
* @return
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
if (clickListener != null) {
clickListener.onDoubleClick(e);
}
return super.onDoubleTap(e);
}
/**
* 常按事件监听
* @param e
*/
@Override
public void onLongPress(MotionEvent e) {
if (clickListener != null) {
clickListener.onLongPressClick(e);
}
super.onLongPress(e);
}
};
同时设置了点击事件的监听接口回调
/**
* 控件 点击事件监听 回调
*/
public interface OnWaveProgressClickListener {
/**
* 单击回调接口
* @param e
*/
public void onSingleClick(MotionEvent e);
/**
* 双击回调接口
* @param e
*/
public void onDoubleClick(MotionEvent e);
/**
* 找按回调接口
* @param e
*/
public void onLongPressClick(MotionEvent e);
}
private OnWaveProgressClickListener clickListener;
/**
* 设置控件的点击事件
*
* @param listener
*/
public void setWaveOnClickListener(OnWaveProgressClickListener listener) {
this.clickListener = listener;
}
4、测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
/**
* 绘制的为正方形进度条,测量比较较小的长度为圆形的直径长度
*/
int width;
int height;
width = Math.min(widthSize, heightSize);
height = Math.min(widthSize, heightSize);
setMeasuredDimension(width, height);
}
5、绘制
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制整体圆形
mCanvas.drawCircle(viewWidth / 2, viewWidth / 2, viewWidth / 2, mCicrlPaint);
//绘制进度图形
mPath.reset();
//进度图形的高度 随着加载进度的改变
float y = (1 - (float) currentProgress / maxProgress) * viewHeight;
mPath.moveTo(viewWidth, y);
mPath.lineTo(viewWidth, viewHeight);
mPath.lineTo(0, viewHeight);
mPath.lineTo(0, y);
//
/**
* 计算振幅的比例
* 振幅 的大小 是随着进度的大小动态改变的
*/
int amplitude = (int) ((1 - (float) currentProgress / maxProgress) * mDefaulAmplitude);
if (currentProgress % 2 == 0) {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
}
} else {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
}
}
mPath.close();
mCanvas.drawPath(mPath, mProgressPaint);
//
//绘制 中间显示的百分比文字
String text = "" + (int) (((float) currentProgress / maxProgress) * 100);
//获取绘制文字的宽度
float textWidth = mTextPaint.measureText(text);
//获取绘制文字的高度
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
//Descent是baseline之下至字符最低处的距离
//Ascent是baseline之上至字符最高处的距离
//ascent + descent 就是测量文字的高度
float textHeight = fontMetrics.ascent + fontMetrics.descent;
//计算绘制中间显示进度文字的坐标
float textY = viewHeight / 2 - textHeight / 2;
float textX = viewWidth / 2 - textWidth / 2;
//绘制显示进度的文字
mCanvas.drawText(text, textX, textY, mTextPaint);
canvas.drawBitmap(bitmap, 0, 0, null);
}
绘制分析
5.1绘制圆形背景
5.2绘制进度
5.3绘制波浪曲线
曲线分析 一
曲线分析 二
曲线分析三
绘制:
/**
* 计算振幅的比例
* 振幅 的大小 是随着进度的大小动态改变的
*/
int amplitude = (int) ((1 - (float) currentProgress / maxProgress) * mDefaulAmplitude);
if (currentProgress % 2 == 0) {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
}
} else {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
}
}
效果
上面的图形中绘制了四组正反曲线,也就是说分别绘制了四个开口向上的曲线,四个开口向下的曲线,
这里采用的绘制思想是每一次都 绘制一个开口向上的曲线 和一个开口向下的曲线,然后形成一个完整的类似正弦曲线的线形,这里绘制了四次,也就是在FOR循环中循环了四次,具体的循环次数是不定的,依据控件的大小来动态设置
5.4绘制显示文本
//获取绘制文字的宽度
float textWidth = mTextPaint.measureText(text);
//获取绘制文字的高度
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
//Descent是baseline之下至字符最低处的距离
//Ascent是baseline之上至字符最高处的距离
//ascent + descent 就是测量文字的高度
float textHeight = fontMetrics.ascent + fontMetrics.descent;
//计算绘制中间显示进度文字的坐标
float textY = viewHeight / 2 - textHeight / 2;
float textX = viewWidth / 2 - textWidth / 2;
//绘制显示进度的文字
mCanvas.drawText(text, textX, textY, mTextPaint);
其中涉及到一些文本测量
6、完整源码
package com.animation.androidlongs.a360animationapplication.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
/**
* Created by androidlongs on 16/8/9.
*/
public class WaveProgressView extends View {
private Paint mCicrlPaint;
private Paint mProgressPaint;
private Paint mTextPaint;
private Canvas mCanvas;
private Path mPath;
private Bitmap bitmap;
public WaveProgressView(Context context) {
super(context);
init(context);
}
public WaveProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public WaveProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
//绘制圆形的 Paint
mCicrlPaint = new Paint();
mCicrlPaint.setAntiAlias(true);
mCicrlPaint.setColor(Color.BLUE);
//绘制进度的 Paint
mProgressPaint = new Paint();
mProgressPaint.setAntiAlias(true);
mProgressPaint.setColor(Color.RED);
//设置只绘制重叠的部分
mProgressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//绘制文字的 Paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTextSize(20);
//绘制进度的Path
mPath = new Path();
/**
* 手势识别监听
*/
final GestureDetector gestureDetector = new GestureDetector(listener);
/**
* 将触摸识别事件传递给 GestureDetector
*/
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
this.setClickable(true);
//设置布局加载监听
this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//更新控件的大小
viewHeight = WaveProgressView.this.getHeight();
viewWidth = WaveProgressView.this.getWidth();
//创建 bitmap 与 canvas
bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(bitmap);
/**
* 更新默认设置
*/
updateDefaulBuildValue();
}
});
}
GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
/**
* 单击点击监听
* @param e
* @return
*/
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
System.out.println("sing click ");
if (clickListener != null) {
clickListener.onSingleClick(e);
return true;
}
return super.onSingleTapConfirmed(e);
}
/**
* 双击点击监听
* @param e
* @return
*/
@Override
public boolean onDoubleTap(MotionEvent e) {
if (clickListener != null) {
clickListener.onDoubleClick(e);
}
return super.onDoubleTap(e);
}
/**
* 常按事件监听
* @param e
*/
@Override
public void onLongPress(MotionEvent e) {
if (clickListener != null) {
clickListener.onLongPressClick(e);
}
super.onLongPress(e);
}
};
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
/**
* 绘制的为正方形进度条,测量比较较小的长度为圆形的直径长度
*/
int width;
int height;
width = Math.min(widthSize, heightSize);
height = Math.min(widthSize, heightSize);
setMeasuredDimension(width, height);
}
/**
* 默认控件的 宽 高
* 默认的 当前的进度 总的进度
*/
public int viewWidth = 150;
public int viewHeight = 150;
public int currentProgress = 0;
public int maxProgress = 100;
/**
* 计算振幅大小 所用的比例数 默认为10
*/
private int mAplitudeCunt = 10;
/**
* 默认振幅的大小 为控件高度的 1/10
*/
private int mDefaulAmplitude = viewHeight / mAplitudeCunt;
/**
* 计算周期长度使用的比例数 默认为8
*/
private int mPriodicCount = 8;
/**
* 曲线默认的周期长度
*/
private int mDefaulPriodic = viewHeight / mPriodicCount;
/**
* 曲线的绘制次数
*/
private int mquadLineDrawCount = (int) ((float) viewWidth / mDefaulPriodic);
/**
* 更新默认的设置
*/
private void updateDefaulBuildValue() {
if (viewHeight < 400) {
mPriodicCount = 8;
mAplitudeCunt = 10;
} else if (viewHeight < 600 && viewHeight >= 400) {
mPriodicCount = 14;
mAplitudeCunt = 16;
} else if (viewHeight < 1000 && viewHeight >= 600) {
mPriodicCount = 20;
mAplitudeCunt = 20;
} else if (viewHeight < 1400 && viewHeight >= 100) {
mAplitudeCunt = 24;
mPriodicCount = 26;
} else {
mPriodicCount = 30;
mAplitudeCunt = 30;
}
/**
* 默认振幅的大小 为控件高度的 1/10
*/
mDefaulAmplitude = viewHeight / mAplitudeCunt;
/**
* 曲线默认的周期长度
*/
mDefaulPriodic = viewHeight / 8;
/**
* 曲线的绘制次数
*/
mquadLineDrawCount = (int) ((float) viewWidth / mDefaulPriodic);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制整体圆形
mCanvas.drawCircle(viewWidth / 2, viewWidth / 2, viewWidth / 2, mCicrlPaint);
//绘制进度图形
mPath.reset();
//进度图形的高度 随着加载进度的改变
float y = (1 - (float) currentProgress / maxProgress) * viewHeight;
mPath.moveTo(viewWidth, y);
mPath.lineTo(viewWidth, viewHeight);
mPath.lineTo(0, viewHeight);
mPath.lineTo(0, y);
//
/**
* 计算振幅的比例
* 振幅 的大小 是随着进度的大小动态改变的
*/
int amplitude = (int) ((1 - (float) currentProgress / maxProgress) * mDefaulAmplitude);
if (currentProgress % 2 == 0) {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
}
} else {
for (int i = 0; i < mquadLineDrawCount; i++) {
mPath.rQuadTo(mDefaulPriodic / 2, amplitude, mDefaulPriodic, 0);
mPath.rQuadTo(mDefaulPriodic / 2, -amplitude, mDefaulPriodic, 0);
}
}
mPath.close();
mCanvas.drawPath(mPath, mProgressPaint);
//
//绘制 中间显示的百分比文字
String text = "" + (int) (((float) currentProgress / maxProgress) * 100);
//获取绘制文字的宽度
float textWidth = mTextPaint.measureText(text);
//获取绘制文字的高度
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
//Descent是baseline之下至字符最低处的距离
//Ascent是baseline之上至字符最高处的距离
//ascent + descent 就是测量文字的高度
float textHeight = fontMetrics.ascent + fontMetrics.descent;
//计算绘制中间显示进度文字的坐标
float textY = viewHeight / 2 - textHeight / 2;
float textX = viewWidth / 2 - textWidth / 2;
//绘制显示进度的文字
mCanvas.drawText(text, textX, textY, mTextPaint);
canvas.drawBitmap(bitmap, 0, 0, null);
}
/**
* 控件 点击事件监听 回调
*/
public interface OnWaveProgressClickListener {
/**
* 单击回调接口
*
* @param e
*/
public void onSingleClick(MotionEvent e);
/**
* 双击回调接口
*
* @param e
*/
public void onDoubleClick(MotionEvent e);
/**
* 找按回调接口
*
* @param e
*/
public void onLongPressClick(MotionEvent e);
}
private OnWaveProgressClickListener clickListener;
/**
* 控件 加载进度回调接口
*/
public interface OnWaveProgressListener {
public void onProgresUpdate(int progress);
public void onFinish();
}
private OnWaveProgressListener progressListener;
/**
* 设置更新当前进度
*
* @param progress
*/
public void updateProgress(int progress) {
if (progress <= 0) {
progress = 0;
} else if (progress > this.maxProgress) {
progress = maxProgress;
}
this.currentProgress = progress;
//进度更新接口回调
if (progressListener != null) {
progressListener.onProgresUpdate(currentProgress);
}
if (progress == maxProgress) {
if (progressListener != null) {
progressListener.onFinish();
}
}
//重绘
invalidate();
}
/**
* 设置最大进度
*
* @param max
*/
public void setMaxProgress(int max) {
if (max <= 0) {
max = 100;
}
this.setMaxProgress(max);
}
/**
* 设置控件的点击事件
*
* @param listener
*/
public void setWaveOnClickListener(OnWaveProgressClickListener listener) {
this.clickListener = listener;
}
/**
* 加载进度监听设置
*
* @param listener
*/
public void setWaveOnProgressListener(OnWaveProgressListener listener) {
this.progressListener = listener;
}
}
7、Activity中的使用
package com.animation.androidlongs.a360animationapplication;
import android.content.Intent;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import com.animation.androidlongs.a360animationapplication.view.WaveProgressView;
public class MainActivity extends AppCompatActivity {
private WaveProgressView customWaveProgressView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, FloatViewService.class);
//startService(intent);
customWaveProgressView = (WaveProgressView) findViewById(R.id.progress);
handler.postDelayed(runnable,2000);
//设置点击事件监听
customWaveProgressView.setWaveOnClickListener(new WaveProgressView.OnWaveProgressClickListener() {
@Override
public void onSingleClick(MotionEvent e) {
System.out.println("onSingleClick");
}
@Override
public void onDoubleClick(MotionEvent e) {
System.out.println("onDoubleClick");
}
@Override
public void onLongPressClick(MotionEvent e) {
System.out.println("onLongPressClick");
}
});
//设置进度更新监听
customWaveProgressView.setWaveOnProgressListener(new WaveProgressView.OnWaveProgressListener() {
@Override
public void onProgresUpdate(int progress) {
}
@Override
public void onFinish() {
}
});
}
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
currentNum++;
customWaveProgressView.updateProgress(currentNum);
if (currentNum
handler.postDelayed(runnable,200);
}else {
handler.removeCallbacks(runnable);
currentNum = 0;
}
}
};
private int maxNum = 100;
private int currentNum =0;
}