Android 圆盘含进度条播放自定义view:
效果图:
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
/**
* <p>
* Title: 项目名称_[子系统统名]_[模块名]
* </p>
* <p>
* Description: [旋转cd控件]
* </p>
*
* @author baigaolin
* @author (lastest modification by $Author$)
* @version $Revision$ 2019-x-x
* @since 2019
*/
public class RotateCDView extends View {
private static final int MSG_RUN = 0x00000100;
private static final int TIME_UPDATE = 16;
private Bitmap mClipBitmap;//cd图片
private Matrix mMatrix;
private float mRotation = 0.0f;
private volatile boolean isRunning;
private Paint paint;
private int ringColor;
private int ringProgressColor;
private float ringWidth;
private boolean ringProgressColorStatus; // 圆环进度状态是固定值还是渐变(false—渐变;true—固定颜色),如果是渐变则必须设置以下三个参数,否则只设置ringProgressColor
private int ringStartProgressColor;// 圆环进度初始色值
private int ringEndProgressColor;// 圆环进度结束色值
private double scale;
public RotateCDView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mMatrix = new Matrix();
paint = new Paint();
//属性
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar);
ringColor = mTypedArray.getColor(R.styleable.RoundProgressBar_ringColor,0xff50c0e9);
ringProgressColorStatus = mTypedArray.getBoolean(R.styleable.RoundProgressBar_ringProgressColorStatus, true);
if(!ringProgressColorStatus) {
ringStartProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_startProgressColor, Color.parseColor("#eb5948"));
ringEndProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_stopProgressColor, Color.parseColor("#ea402e"));
} else {
ringProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_ringProgressColor, 0xffffc641);
}
ringWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_ringWidth, 20);
mTypedArray.recycle();
}
public RotateCDView(Context context, AttributeSet attrs) {
this(context, attrs ,0);
}
public RotateCDView(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mClipBitmap == null){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int width = 0;
int height = 0;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else {
width = mClipBitmap.getWidth();
//子view不能大于父类
if (widthMode == MeasureSpec.AT_MOST){
width = Math.min(width,widthSize);
}
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = mClipBitmap.getHeight();
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
if (mClipBitmap == null)
return;
canvas.save();
//以图片中心作为旋转中心,旋转 mRotation°
mMatrix.setRotate(mRotation, mClipBitmap.getWidth() / 2, mClipBitmap.getHeight() / 2);
// mMatrix.setRotate(mRotation, (float) (DisplayUtil.getScreenWidth(AppApplication.getAppContext())/10*1.5), (float) (DisplayUtil.getScreenWidth(AppApplication.getAppContext())/10*1.5));
canvas.drawBitmap(mClipBitmap,mMatrix,null);
canvas.restore();
int center = (int)(mClipBitmap.getWidth() / 2);//圆心的x坐标
// int center = (int)(DisplayUtil.getScreenWidth(AppApplication.getAppContext())/2*scale);//圆心的x坐标
int radius = (int)(center-ringWidth/2);
/**
* 画最外层的大圆环
*/
paint.setColor(ringColor);//设置圆环的颜色
paint.setStyle(Paint.Style.STROKE);//设置空心
paint.setStrokeWidth(ringWidth); //设置圆环的宽度
paint.setAntiAlias(true); //消除锯齿
canvas.drawCircle(center, center, radius, paint); //画出圆环
paint.setStrokeWidth(ringWidth);
if(!ringProgressColorStatus) { // 渐变方式
//得到 当前所占百分比的渐变值
float floatValue = this.mRotation / 360;
//颜色估值器
ArgbEvaluator evaluator = new ArgbEvaluator();
//得到进度条渐变色
int evaluate = (int) evaluator.evaluate(floatValue, ringStartProgressColor,ringEndProgressColor);
ringProgressColor = evaluate;
}
paint.setColor(ringProgressColor);
RectF oval = new RectF(center - radius, center - radius, center
+ radius, center + radius);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND); // 设置画笔笔刷类型 如影响画笔但始末端,如圆形样式
canvas.drawArc(oval, -90, mRotation, false, paint); //根据进度画圆弧 -90为从12点处开始
// float angleOfMoveCircle = -90f + mRotation;
//
// // 进度条开始加小圆,以便实现进度条两头都是圆角
// canvas.drawCircle(
// (float) (center + radius * Math.cos(-88 * 3.14 / 180)),
// (float) (center + radius * Math.sin(-88 * 3.14 / 180)),
// ringWidth / 40,
// paint);// 小圆
//
// if (mRotation > ringWidth/20) {
//
// // 进度条另一端加小圆,以便实现进度条两头都是圆角
// canvas.drawCircle(
// (float) (center + radius * Math.cos(angleOfMoveCircle * 3.14 / 180)),
// (float) (center + radius * Math.sin(angleOfMoveCircle * 3.14 / 180)),
// ringWidth / 40,
// paint);// 小圆
// }
}
private Bitmap cretaeCircleBitmap(Bitmap src){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setARGB(255,241,239,229);
Bitmap target = Bitmap.createBitmap(getMeasuredWidth(),
getMeasuredHeight(), Bitmap.Config.ARGB_8888);
// 生成白色的
paint.setColor(Color.WHITE);
Canvas canvas = new Canvas(target);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredWidth() / 2,
(float) getMeasuredWidth()/2-ringWidth, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(src, 0, 0, paint);
return target;
}
public void setCdImage(Bitmap bitmap,double scale){
this.scale = scale;
bitmap = scaleBitmap(bitmap, (int) (DisplayUtil.getScreenWidth(AppApplication.getAppContext()) * scale));
int widthSize = bitmap.getWidth();
int heightSize = bitmap.getHeight();
int widthSpec = MeasureSpec.makeMeasureSpec(widthSize,
MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(heightSize,
MeasureSpec.AT_MOST);
measure(widthSpec, heightSpec);
mClipBitmap = cretaeCircleBitmap(bitmap);
requestLayout();
invalidate();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
isRunning = false;
}
// 旋转
public void startRoll(){
if (isRunning)
return;
isRunning = true;
mHandler.sendEmptyMessageDelayed(MSG_RUN, TIME_UPDATE);
}
//暂停旋转
public void pause() {
if (!isRunning)
return;
isRunning = false;
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == MSG_RUN) {
if (isRunning) {
if (mRotation >= 360)
mRotation = 0;
invalidate();
sendEmptyMessageDelayed(MSG_RUN, TIME_UPDATE);
}
}
}
};
public void rotate(float angle){
this.mRotation = (float) (angle*3.6);
}
/**
* 缩放图片
* @param bmp
* @param size 新位图期望的宽度
* @return
*/
public static Bitmap scaleBitmap(Bitmap bmp, int size) {
return Bitmap.createScaledBitmap(bmp, size, size, true);
}
/**
* 计算渐变颜色值 ARGB
*
* @param fraction
* 变化比率 0~1
* @param startValue
* 初始色值
* @param endValue
* 结束色值
* @return
*/
public static Integer evaluate(float fraction, Object startValue, Integer endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
| (int) ((startR + (int) (fraction * (endR - startR))) << 16)
| (int) ((startG + (int) (fraction * (endG - startG))) << 8)
| (int) ((startB + (int) (fraction * (endB - startB))));
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 阴影背景 -->
<hg.zp.ui.widget.ShadowLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:sl_cornerRadius="39dp"
app:sl_dx="1dp"
app:sl_dy="1dp"
android:background="@drawable/bg_float_music_border_radius39dp"
app:sl_shadowColor="@color/default_shadow_color">
<LinearLayout
android:id="@+id/float_window_lay"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_float_music_border_radius39dp"
android:orientation="horizontal" >
<!-- 自定义旋转CD控件 ringColor 圆环的颜色 、ringProgressColor 圆环进度的颜色 ;ringWidth 圆环宽度 -->
<hg.zp.ui.widget.RotateCDView
android:id="@+id/cdView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:ringWidth="4dp"
app:ringColor="@color/float_music_ring_color"
app:ringProgressColorStatus="false"
app:startProgressColor="@color/float_music_ring_start_color"
app:stopProgressColor="@color/float_music_ring_end_color"/>
<ImageView
android:id="@+id/iv_play"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginLeft="18dp"
android:layout_marginRight="16dp"
android:layout_gravity="center"
android:src="@mipmap/music_controls_play" />
<!--播放状态暂存 -->
<TextView
android:id="@+id/music_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:visibility="gone"/>
<ImageView
android:id="@+id/iv_play_close"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="center"
android:layout_marginBottom="1dp"
android:layout_marginRight="20dp"
android:src="@mipmap/music_controls_close" />
</LinearLayout>
</hg.zp.ui.widget.ShadowLayout>
</LinearLayout>
初始化:
LinearLayout mFloatLayout;
WindowManager.LayoutParams mWparams;
static WindowManager mWindowManager;
RotateCDView mRotateView;
public final static int DISTANCE = 15;
public final static int LONG_CLICK_TIME = 1000;
private ProgressRecevier mReceiver;
ImageView mPayImgBtn;
ImageView mCloseImgBtn;
AudioEntity audioEntity;
int progress = 0;
int order = -1;
/**
* 初始化
*/
private void createFloatView() {
mWparams = new WindowManager.LayoutParams();
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
/**
* Android实现浮窗时报的android.view.WindowManager$BadTokenException: Unable to add window异常
* 解决方案:参考https://blog.csdn.net/xiangzaixiansheng/article/details/78830248
*/
if (Build.VERSION.SDK_INT >= 26) {//8.0新特性
// mWparams.type= WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
// mWparams.type = WindowManager.LayoutParams.TYPE_PHONE;
// mWparams.type= 0x7d5; // 把WindowManager.LayoutParams.type字段设成0x7d5, 官网上写了0x000007d5是WindowManager.LayoutParams.TYPE_TOAST的值.
mWparams.type= WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else if(Build.VERSION.SDK_INT >= 23 && Build.VERSION.SDK_INT < 26) {
mWparams.type= WindowManager.LayoutParams.TYPE_PHONE; // 悬浮窗口设置可以为TYPE_PHONE,这种类型是用于提供用户交互操作的非应用窗口。
} else{
mWparams.type= WindowManager.LayoutParams.TYPE_TOAST; // 系统提示。它总是出现在应用程序窗口之上。
// mWparams.type= WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; // 系统提示。它总是出现在应用程序窗口之上。
}
//设置效果为背景透明.
mWparams.format = PixelFormat.RGBA_8888;
mWparams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 让window不能获得焦点,这样用户快就不能向该window发送按键事件及按钮事件
mWparams.gravity = Gravity.LEFT|Gravity.BOTTOM;
// mWparams.gravity = Gravity.LEFT|Gravity.TOP;
// 起始坐标点
// mWparams.x = 0;
// mWparams.y = 0;
// 动态获得home底部高度
mWparams.x = 31;
mWparams.y = DisplayUtil.dip2px(getApplication(), 60);
mWparams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWparams.height = WindowManager.LayoutParams.WRAP_CONTENT;
LayoutInflater inflater = LayoutInflater.from(getApplication());
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.activity_float_music_play,null,false);
try {
mWindowManager.addView(mFloatLayout,mWparams);
} catch (Exception e) {
LogUtils.logi("mWindowManager.addView Exception"+e.getMessage());
}
mRotateView = (RotateCDView) mFloatLayout.findViewById(R.id.cdView);
mPayImgBtn = (ImageView) mFloatLayout.findViewById(R.id.iv_play);
mCloseImgBtn = (ImageView) mFloatLayout.findViewById(R.id.iv_play_close);
mFloatLayout.setGravity(View.GONE); // 默认隐藏
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.mipmap.music_controls_icon);
mRotateView.setCdImage(bmp,0.168); // 圆圈大小控制
mRotateView.startRoll();
。。。。。。。
参考其他博文:https://blog.csdn.net/xiangzaixiansheng/article/details/78830248