Android 圆盘含进度条播放自定义view

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值