360加速球练手项目

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

此项目为本人刚学习Android时的练手项目,技术含量不高,如文中代码书写不够严谨处,望各位见谅。


效果图:
在这里插入图片描述

一、加速球项目总体实现思路

注:悬浮窗体是居于浮窗管理者FloatViewManager实现的,是需要在当前窗口添加我们自定义的小球。

  1. 创建浮窗管理者FloatViewManager
  2. 制作浮窗小球FloatCircleView
  3. 显示浮窗小球FloatCircleView
  4. 处理浮窗小球的触摸和拖动事件在这里插入图片描述
  5. 制作和显示加速球FloatMenuView
  6. 处理加速球FloatMenuView的事件
  7. 加速球FloatMenuView的单击和双击动画
  8. 在底部菜单栏显示加速球FloatMenuView
    在这里插入图片描述

二、实现步骤

第一步.创建浮窗管理者FloatViewManager

FloatViewManager类代码如下(示例):

    private final WindowManager wm;
    public Context context;
  /**
     * (第一步)悬浮球管理者
     * @param context
     */
    public FloatViewManager(Context context) {
        this.context = context;
        wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//1.获取WindowManager实例

    }
    /**
     * 第一步:获取悬浮球的单例对象
     * @param context
     * @return
     */
    public static FloatViewManager getInstance(Context context){
        if (instance==null){
            synchronized (FloatViewManager.class){
                if (instance==null){
                    instance=new FloatViewManager(context);
                }
            }
        }
        return instance;
    }

第二步.制作浮窗小球FloatCircleView

FloatCircleView类代码如下(示例):

/**
 1. 自定义悬浮球样式
 */
public class FloatCircleView extends View {
    private int width=200;
    private int height=200;
    private Paint circlePaint;//画圆的笔
    private Paint textPaint;//画内容的笔

    private String text="50%";

    private Boolean isDrag=false;//判断是否是拖拉效果
    private Bitmap bitmap;

    public FloatCircleView(Context context) {
        super(context);
        initPaints();//初始化画笔
    }

    public FloatCircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaints();//初始化画笔
    }

    public FloatCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaints();//初始化画笔
    }


    public FloatCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initPaints();//初始化画笔
    }

    /**
     * 创建自定义圆形的样式
     */
    private void initPaints() {
        circlePaint = new Paint();
        circlePaint.setColor(Color.BLUE);
        circlePaint.setAntiAlias(true);//启用circlePaint的抗锯齿功能,使绘制的边缘更加平滑。

        textPaint = new Paint();
        textPaint.setTextSize(25);
        textPaint.setColor(Color.WHITE);
        textPaint.setAntiAlias(true);//启用textPaint的抗锯齿功能,使绘制的边缘更加平滑。
        textPaint.setFakeBoldText(true);//设置textPaint使文本显示为假粗体,这会在文本的笔画两端添加一些额外的宽度,让文本看起来更粗

        Bitmap src = BitmapFactory.decodeResource(getResources(), R.mipmap.img);
        //适配大小
        bitmap = Bitmap.createScaledBitmap(src, width, height, true);//创建一个缩放后的位图bitmap,原始位图src被缩放到指定的宽度和高度,
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       setMeasuredDimension(width,height);//setMeasuredDimension是自定义视图的大小
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (isDrag){
            canvas.drawBitmap(bitmap,0,0,null);//使用小火箭
        }else {
            canvas.drawCircle(width/2,height/2,width/2,circlePaint);//1.前面两个参数表示的是圆的圆心  半径为width/2
            float textWidth = textPaint.measureText(text);//2.measureText 方法来测量字符串 text 的宽度
            float x = width / 2 - textWidth / 2;//3.从画布中心点向左偏移 textWidth / 2 的距离。
            Paint.FontMetrics metrics = textPaint.getFontMetrics();//4.这行代码获取 textPaint 画笔的字体度量信息,存储在 metrics 变量中。字体度量信息包括文本的上升、下降、顶部、底部等尺寸。
            float dy=-(metrics.descent+metrics.ascent)/2;//5.这行代码计算文本绘制的垂直偏移量 dy,以确保文本垂直居中。metrics.descent 是文本下降的最大距离,metrics.ascent 是文本上升的最大距离。将两者相加后除以2,得到文本基线到画布中心点的垂直偏移量。
            float y = height / 2 + dy;//6.从画布中心点向上偏移 dy 的距离。
            canvas.drawText(text,x,y,textPaint);//7.文本的起始点坐标为 (x, y)
        }
    }
}

第三步.显示浮窗小球FloatCircleView

1.FloatViewManager类代码如下(示例):

/**
 * 显示悬浮球
 */
public void showFloatCircleView(){

    params=new WindowManager.LayoutParams();//1.新的 WindowManager.LayoutParams 对象,用于定义如何将视图添加到窗口管理器。
    params.width=circleView.getWidth();//2.设置了浮动视图的宽度和高度,它们分别等于 circleView 的宽度和高度
    params.height=circleView.getHeight();
    params.gravity= Gravity.TOP | Gravity.LEFT;//3.设置浮动的位置
    params.x=0;//4.浮动视图的初始x和y坐标,这里都设置为0,意味着视图将从屏幕左上角开始。
    params.y=20;

    //5.设置浮动视图的类型
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    }else if (Build.VERSION.SDK_INT > 24) {
        params.type = WindowManager.LayoutParams.TYPE_PHONE;
    } else {
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
    }
    //6.FLAG_NOT_FOCUSABLE 表示视图不会获取焦点,FLAG_NOT_TOUCH_MODAL 表示当触摸事件发生在该视图上时,不会阻塞其他视图的触摸事件。
    params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    params.format= PixelFormat.RGBA_8888;
    wm.addView(circleView,params);//7.添加到窗口

}
  1. MainActivity 类代码如下(示例):
 public class MainActivity extends AppCompatActivity {

    private Button show_float;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Objects.requireNonNull(getSupportActionBar()).hide();//去掉标题栏

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }

    private void initView() {
        show_float = findViewById(R.id.show_float);
    }

    private void initData() {
        show_float.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this, MyFloatService.class);
                startService(intent);
//                finish();
            }
        });
    }

}
  1. MyFloatService 类代码如下(示例):
 public class MyFloatService extends Service {
 //该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        FloatViewManager floatViewManager=FloatViewManager.getInstance(this);
        floatViewManager.showFloatCircleView();//展示悬浮球
    }
}

第四步.处理浮窗小球的触摸和拖动事件

FloatViewManager类代码如下(示例):

  private View.OnTouchListener circleViewTouchListener=new View.OnTouchListener() {

        private float y0;
        private float x0;
        private float startY;

        /**
         * @param v     The view the touch event has been dispatched to.
         * @param event The MotionEvent object containing full information about
         *              the event.
         * @return
         */
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    //1.移动前记录一下起始位置
                    startX = event.getRawX();
                    startY = event.getRawY();
                    //TODO 为了消费点击事件
                    x0 = event.getRawX();
                    y0 = event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //1.记录移动之后所在的位置
                    float x = event.getRawX();
                    float y = event.getRawY();
                    //2.记录移动的偏移量
                    float dx = x - startX;
                    float dy = y - startY;
                    //3.设置在窗口
                    params.x+=dx;
                    params.y+=dy;
                    //3.1移动的时候,悬浮球形状改变
                    circleView.setDragStage(true);
                    //4.刷新当前位置
                    wm.updateViewLayout(circleView,params);
                    //5.设置当前位置
                    startX=x;
                    startY=y;
                    Log.d("TAG", "偏移了: "+event);
                    break;
                case MotionEvent.ACTION_UP:
                    //1.记录当前的位置
                    float endX = event.getRawX();
                    //2.悬浮球只能靠左或者靠右
                    if (endX>getScreenWidth()/2){
                        params.x=getScreenWidth()-circleView.getWidth();
                    }else {
                        params.x=0;//表示悬浮球所处在屏幕的位置
                    }
                    circleView.setDragStage(false);
                    wm.updateViewLayout(circleView,params);

                    //TODO 判断是否消费当前点击事件
                    if (Math.abs(endX-x0)>6){
                        return false;
                    }else {
                        return false;
                    }

                default:
                    break;
            }
            return false;
        }
    };

    //获取屏幕的宽度
    private int getScreenWidth(){
        return wm.getDefaultDisplay().getWidth();
    }

    /**
     * (第一步)悬浮球管理者
     * @param context
     */
    public FloatViewManager(Context context) {
        this.context = context;
        wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//1.获取WindowManager实例

        circleView= new FloatCircleView(context);

        circleView.setOnTouchListener(circleViewTouchListener);

        circleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context,"点击悬浮球",Toast.LENGTH_SHORT).show();
                //隐藏circleView 显示菜单栏 开启动画
                wm.removeView(circleView);
                showFloatMenuView();
                floatMenuView.startAnimation();
            }
        });

        floatMenuView = new FloatMenuView(context);
    }

关键代码:

//3.设置在窗口
params.x+=dx;
params.y+=dy;
//3.1移动的时候,悬浮球形状改变
circleView.setDragStage(true);
//4.刷新当前位置
wm.updateViewLayout(circleView,params);

每一步都要更新当前位置

//TODO 判断是否消费当前点击事件
if (Math.abs(endX-x0)>6){
    return false;
}else {
    return false;
}

如果是在拖拽的话,则当前就不是点击或者双击事件,要处理好事件的冲突问题

第五步.制作和显示加速球FloatMenuView

  1. FloatCircleView类代码如下(示例):
/**
 1. 自定义加速球
 */
public class FloatMenuView extends LinearLayout {

    private LinearLayout ll;
    private TranslateAnimation animation;

    public FloatMenuView(Context context) {
        super(context);
                View root = View.inflate(getContext(), R.layout.float_menu_view, null);
        ll = root.findViewById(R.id.ll);
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,0,
                Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0);
        animation.setDuration(500);
        animation.setFillAfter(true);
        ll.setAnimation(animation);
        root.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                FloatViewManager manager=FloatViewManager.getInstance(context);//1.获取浮窗管理者实例
                manager.hideFloatMenuView();//2.点击空白处,隐藏加速球,且显示浮窗小球
                manager.showFloatCircleView();
                return false;//触摸事件没有被消耗,允许事件继续传递。(还可以继续点击浮窗小球)
            }
        });
        addView(root);
    }

    //执行动画
    public void startAnimation(){
        animation.start();
    }

    public FloatMenuView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        View root = View.inflate(getContext(), R.layout.float_menu_view, null);
        addView(root);
    }

    public FloatMenuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View root = View.inflate(getContext(), R.layout.float_menu_view, null);
        addView(root);
    }

    public FloatMenuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}
  1. FloatViewManager类的代码如下

    public FloatViewManager(Context context) {
        this.context = context;
        wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//1.获取WindowManager实例

        circleView= new FloatCircleView(context);

        circleView.setOnTouchListener(circleViewTouchListener);

        circleView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context,"点击悬浮球",Toast.LENGTH_SHORT).show();
                //隐藏circleView 显示菜单栏 开启动画
                wm.removeView(circleView);
                showFloatMenuView();
                floatMenuView.startAnimation();
            }
        });

        floatMenuView = new FloatMenuView(context);
    }
   /**
     * 加速球的显示
     */
    private void showFloatMenuView() {
        WindowManager.LayoutParams params=new WindowManager.LayoutParams();
        params.width=getScreenWidth();
        params.height=getScreenHeight()-getStateHeight();
        params.gravity=Gravity.BOTTOM | Gravity.LEFT;
        params.x=0;
        params.y=0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        }else if (Build.VERSION.SDK_INT > 24) {
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_TOAST;
        }
        params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        params.format= PixelFormat.RGBA_8888;
        wm.addView(floatMenuView,params);
    }
    //隐藏加速球
    public void hideFloatMenuView() {
        wm.removeView(floatMenuView);
    }

第六步.处理加速球FloatMenuView的事件

MyProcessView 类代码如下(示例):

public class MyProcessView extends View {

    private int width=200;
    private int height=200;
    private Bitmap bitmap;
    private Canvas bitmapCanvas;
    private Paint circlePaint;
    private Paint progressPaint;
    private Paint textPaint;

    private Path path=new Path();

    private int progress=50;//最高进度
    private int currentProgress=0;//当前的进度
    private int max=100;
    private GestureDetector detector;
    private int count=50;
    private boolean isSingleTag=false;
    
    //更新UI线程
    private Handler handler=new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    public MyProcessView(Context context) {
        super(context);
        init();
    }
    public MyProcessView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyProcessView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public MyProcessView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    /**
     * 设置绘制所需的画笔、位图和触摸监听器
     */
    private void init() {
        //1.绘制圆
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);
        circlePaint.setColor(Color.argb(0xff,0x3a,0x8b,0x6c));

        //2.绘制进度效果
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setColor(Color.argb(0xff,0x4e,0x5d,0x6f));
        progressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//混合模式   遮挡的效果

        //3.绘制文本
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(25);

        bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        bitmapCanvas = new Canvas(bitmap);

        //手势检测器——目的是为了检测是单击还是双击
        detector = new GestureDetector(new MyGestureDetectorListener());
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return detector.onTouchEvent(event);
            }
        });
        setClickable(true);//允许点击
    }

    class MyGestureDetectorListener extends GestureDetector.SimpleOnGestureListener{
        @Override
        public boolean onDoubleTap(@NonNull MotionEvent e) {
            Toast.makeText(getContext(),"双击",Toast.LENGTH_SHORT).show();
            startDoubleTapAnimation();//
            return super.onDoubleTap(e);
        }


        @Override
        public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
            Toast.makeText(getContext(),"单击",Toast.LENGTH_SHORT).show();
            isSingleTag=true;
            currentProgress=progress;
            startSingleTapAnimation();
            return super.onSingleTapConfirmed(e);
        }
    }

    private void startSingleTapAnimation() {
        handler.postDelayed(singleTapRunnable,200);
    }
    private SingleTapRunnable singleTapRunnable=new SingleTapRunnable();
    class SingleTapRunnable implements Runnable{

        @Override
        public void run() {
            count--;
            if (count>=0){
                invalidate();
                handler.postDelayed(singleTapRunnable,200);
            }else {
                handler.removeCallbacks(singleTapRunnable);
                count=50;
            }
        }
    }

    private void startDoubleTapAnimation() {
        handler.postDelayed(doubleTapRunnable,50);
    }

    private DoubleTapRunnable doubleTapRunnable=new DoubleTapRunnable();
    class DoubleTapRunnable implements Runnable{

        @Override
        public void run() {
            currentProgress++;
            if (currentProgress<=progress){
                invalidate();
                handler.postDelayed(doubleTapRunnable,50);
            }else {
                handler.removeCallbacks(doubleTapRunnable);
                currentProgress=0;
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        bitmapCanvas.drawCircle(width/2,height/2,width/2,circlePaint);
        path.reset();
        float y = (1 - (float) currentProgress / max) * height;
        path.moveTo(width,y);
        path.lineTo(width,height);
        path.lineTo(0,height);
        path.lineTo(0,y);

        if (!isSingleTag){
            float d = (1 - ((float) currentProgress / progress)) * 10;
            for (int i = 0; i < 5; i++) {//表示的是波浪
                path.rQuadTo(10,-d,20,0);
                path.rQuadTo(10,d,20,0);
            }
        }else {
            float d=(float) count/50*10;
            if (count%2==0){
                for (int i = 0; i < 5; i++) {
                    path.rQuadTo(20,-d,40,0);
                    path.rQuadTo(20,d,40,0);
                }
            }else {
                for (int i = 0; i < 5; i++) {

                    path.rQuadTo(20,d,40,0);
                    path.rQuadTo(20,-d,40,0);
                }
            }
        }

        path.close();
        bitmapCanvas.drawPath(path,progressPaint);
        String text=(int)(((float)currentProgress/max)*100)+"%";
        float textWidth = textPaint.measureText(text);
        Paint.FontMetrics metrics = textPaint.getFontMetrics();
        float baseLine = height / 2 - (metrics.ascent + metrics.descent) / 2;
        bitmapCanvas.drawText(text,width/2-textWidth/2,baseLine,textPaint);
        canvas.drawBitmap(bitmap,0,0,null);
    }
}

第七步.加速球FloatMenuView的单击和双击动画

MyProcessView类代码如下(示例):

private void init() {
    //1.
    circlePaint = new Paint();
    circlePaint.setAntiAlias(true);
    circlePaint.setColor(Color.argb(0xff,0x3a,0x8b,0x6c));

    progressPaint = new Paint();
    progressPaint.setAntiAlias(true);
    progressPaint.setColor(Color.argb(0xff,0x4e,0x5d,0x6f));
    progressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

    textPaint = new Paint();
    textPaint.setAntiAlias(true);
    textPaint.setColor(Color.WHITE);
    textPaint.setTextSize(25);

    bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
    bitmapCanvas = new Canvas(bitmap);

    //监听器
    detector = new GestureDetector(new MyGestureDetectorListener());
    setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return detector.onTouchEvent(event);
        }
    });
    setClickable(true);//允许点击
}

class MyGestureDetectorListener extends GestureDetector.SimpleOnGestureListener{
    @Override
    public boolean onDoubleTap(@NonNull MotionEvent e) {
        Toast.makeText(getContext(),"双击",Toast.LENGTH_SHORT).show();
        startDoubleTapAnimation();//
        return super.onDoubleTap(e);
    }


    @Override
    public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
        Toast.makeText(getContext(),"单击",Toast.LENGTH_SHORT).show();
        isSingleTag=true;
        currentProgress=progress;
        startSingleTapAnimation();
        return super.onSingleTapConfirmed(e);
    }
}

//单击加速球
private void startSingleTapAnimation() {
    handler.postDelayed(singleTapRunnable,200);
}
private SingleTapRunnable singleTapRunnable=new SingleTapRunnable();
class SingleTapRunnable implements Runnable{

    @Override
    public void run() {
        count--;
        if (count>=0){
            invalidate();
            handler.postDelayed(singleTapRunnable,200);
        }else {
            handler.removeCallbacks(singleTapRunnable);
            count=50;
        }
    }
}

private void startDoubleTapAnimation() {
    handler.postDelayed(doubleTapRunnable,50);
}

private DoubleTapRunnable doubleTapRunnable=new DoubleTapRunnable();
class DoubleTapRunnable implements Runnable{

    @Override
    public void run() {
        currentProgress++;
        if (currentProgress<=progress){
            invalidate();
            handler.postDelayed(doubleTapRunnable,50);
        }else {
            handler.removeCallbacks(doubleTapRunnable);
            currentProgress=0;
        }
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(width, height);
}

    /**
     * 自定义绘制一个圆形进度条和动画效果
     * @param canvas the canvas on which the background will be drawn
     */
    @Override
    protected void onDraw(Canvas canvas) {

        bitmapCanvas.drawCircle(width/2,height/2,width/2,circlePaint);//1.绘制一个圆
        path.reset();//2.绘制新的路径
        float y = (1 - (float) currentProgress / max) * height;
        path.moveTo(width,y);//3.当前点移动到视图的最右侧
        path.lineTo(width,height);//绘制一条线到视图的右下角
        path.lineTo(0,height);//绘制一条线到视图的左下角
        path.lineTo(0,y);//绘制一条线回到起始y坐标

        if (!isSingleTag){
            float d = (1 - ((float) currentProgress / progress)) * 10;//4.计算波浪路径的偏移量
            for (int i = 0; i < 5; i++) {//表示的是波浪
                path.rQuadTo(10,-d,20,0);
                path.rQuadTo(10,d,20,0);
            }
        }else {
            float d=(float) count/50*10;//根据count的值计算偏移量
            if (count%2==0){
                for (int i = 0; i < 5; i++) {
                    path.rQuadTo(20,-d,40,0);//绘制反向的二次贝塞尔曲线
                    path.rQuadTo(20,d,40,0);//绘制正向的二次贝塞尔曲线
                }
            }else {
                for (int i = 0; i < 5; i++) {

                    path.rQuadTo(20,d,40,0);
                    path.rQuadTo(20,-d,40,0);
                }
            }
        }

        path.close();//
        bitmapCanvas.drawPath(path,progressPaint);//画笔在bitmapCanvas画布上绘制路径
        String text=(int)(((float)currentProgress/max)*100)+"%";//计算进度百分比,并转换成字符串
        float textWidth = textPaint.measureText(text);//测量文本宽度
        Paint.FontMetrics metrics = textPaint.getFontMetrics();//获取文本的度量信息
        float baseLine = height / 2 - (metrics.ascent + metrics.descent) / 2;//计算文本基线位置,确保文本
        bitmapCanvas.drawText(text,width/2-textWidth/2,baseLine,textPaint);//绘制文本,位置居中
        canvas.drawBitmap(bitmap,0,0,null);//将bitmapCanvas上的绘制结果绘制到主界面
    }

第二步.在底部菜单栏显示加速球FloatMenuView

FloatViewManager类代码如下(示例):

    public int getStateHeight(){
        try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object o = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = (int) field.get(o);
            return context.getResources().getDimensionPixelSize(x);
        } catch (Exception e) {
           return 0;
        }
    }
    /**
     * 加速球的显示
     */
    private void showFloatMenuView() {
        WindowManager.LayoutParams params=new WindowManager.LayoutParams();
        params.width=getScreenWidth();
        params.height=getScreenHeight()-getStateHeight();
        params.gravity=Gravity.BOTTOM | Gravity.LEFT;
        params.x=0;
        params.y=0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        }else if (Build.VERSION.SDK_INT > 24) {
            params.type = WindowManager.LayoutParams.TYPE_PHONE;
        } else {
            params.type = WindowManager.LayoutParams.TYPE_TOAST;
        }
        params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        params.format= PixelFormat.RGBA_8888;
        wm.addView(floatMenuView,params);
    }
---

注:本项目内容尚未完全,后续会根据自己的实际情况进行修改和补充>
项目地址:360加速球

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值