自定义View:悬浮球与加速球

news/2024/11/24 18:36:39/

先来看一张动态图

在这里插入图片描述

昨天跟着视频学了如何自定义View并做成仿360悬浮球与加速球的样式

可以看出来,做成的效果有:

  • 点击按钮后退出Activity,呈现一个圆形的悬浮球,可以随意拖动并会自动依靠到屏幕一侧,且拖动时会变成一张图片
  • 当点击悬浮球时,悬浮球隐藏,底部出现一个加速球,双击加速球时,呈现水量逐渐增高且波动幅度较小的效果,单击时波浪上下波动且幅度渐小
  • 点击屏幕不包含底部加速球的部位,加速球会隐藏,悬浮球重新出现

要做出这么一个效果,需要两个自定义View与一个自定义ViewGroup

在这里插入图片描述

首先,需要先设计悬浮球View——FloatBall
简单起见,为FloatBall指定一个默认宽度和高度——150像素
然后在onDraw(Canvas canvas)方法中,判断FloatBall是否正在被拖动isDrag,如果是,则绘制一张默认图片bitmap,否则则根据绘图函数绘制圆形与居中文本

/*** Created by ZY on 2016/8/10.* 悬浮球*/
public class FloatBall extends View {public int width = 150;public int height = 150;//默认显示的文本private String text = "50%";//是否在拖动private boolean isDrag;private Paint ballPaint;private Paint textPaint;private Bitmap bitmap;public FloatBall(Context context) {super(context);init();}public FloatBall(Context context, AttributeSet attrs) {super(context, attrs);init();}public FloatBall(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public void init() {ballPaint = new Paint();ballPaint.setColor(Color.GRAY);ballPaint.setAntiAlias(true);textPaint = new Paint();textPaint.setTextSize(25);textPaint.setColor(Color.WHITE);textPaint.setAntiAlias(true);textPaint.setFakeBoldText(true);Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.ninja);//将图片裁剪到指定大小bitmap = Bitmap.createScaledBitmap(src, width, height, true);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(width, height);}@Overrideprotected void onDraw(Canvas canvas) {if (!isDrag) {canvas.drawCircle(width / 2, height / 2, width / 2, ballPaint);float textWidth = textPaint.measureText(text);Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();float dy = -(fontMetrics.descent + fontMetrics.ascent) / 2;canvas.drawText(text, width / 2 - textWidth / 2, height / 2 + dy, textPaint);} else {//正在被拖动时则显示指定图片canvas.drawBitmap(bitmap, 0, 0, null);}}//设置当前移动状态public void setDragState(boolean isDrag) {this.isDrag = isDrag;invalidate();}
}

因为FloatBall是不存在于Activity中而在屏幕单独显示的,所以需要用WindowManager来添加View并显示
新建一个类,命名为ViewManager,用来总的管理View的显示与删除
私有化构造函数并采用单例模式

    private static ViewManager manager;//私有化构造函数private ViewManager(Context context) {this.context = context;init();}//获取ViewManager实例public static ViewManager getInstance(Context context) {if (manager == null) {manager = new ViewManager(context);}return manager;}

ViewManager包含有显示与隐藏悬浮球与加速球的函数

//显示浮动小球public void showFloatBall() {if (floatBallParams == null) {floatBallParams = new LayoutParams();floatBallParams.width = floatBall.width;floatBallParams.height = floatBall.height - getStatusHeight();floatBallParams.gravity = Gravity.TOP | Gravity.LEFT;floatBallParams.type = LayoutParams.TYPE_TOAST;floatBallParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL;floatBallParams.format = PixelFormat.RGBA_8888;}windowManager.addView(floatBall, floatBallParams);}//显示底部菜单private void showFloatMenu() {if (floatMenuParams == null) {floatMenuParams = new LayoutParams();floatMenuParams.width = getScreenWidth();floatMenuParams.height = getScreenHeight() - getStatusHeight();floatMenuParams.gravity = Gravity.BOTTOM;floatMenuParams.type = LayoutParams.TYPE_TOAST;floatMenuParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL;floatMenuParams.format = PixelFormat.RGBA_8888;}windowManager.addView(floatMenu, floatMenuParams);}//隐藏底部菜单public void hideFloatMenu() {if (floatMenu != null) {windowManager.removeView(floatMenu);}}

将悬浮球置于Service中开启,这样悬浮球就不那么容易被系统去除了
在onCreate()方法中调用showFloatBall()

public class StartFloatBallService extends Service {public StartFloatBallService() {}@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.throw new UnsupportedOperationException("Not yet implemented");}@Overridepublic void onCreate() {ViewManager manager = ViewManager.getInstance(this);manager.showFloatBall();super.onCreate();}
}

此时,只要为MainActivity添加一个按钮,并设定当点击按钮后开启Service,此时即可看到屏幕显示了一个悬浮球

    public void startService(View view) {Intent intent = new Intent(this, StartFloatBallService.class);startService(intent);finish();}

不过此时悬浮球还不支持拖动与点击,还需要为其添加OnTouchListener与OnClickListener

View.OnTouchListener touchListener = new View.OnTouchListener() {float startX;float startY;float tempX;float tempY;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startX = event.getRawX();startY = event.getRawY();tempX = event.getRawX();tempY = event.getRawY();break;case MotionEvent.ACTION_MOVE:float x = event.getRawX() - startX;float y = event.getRawY() - startY;//计算偏移量,刷新视图floatBallParams.x += x;floatBallParams.y += y;floatBall.setDragState(true);windowManager.updateViewLayout(floatBall, floatBallParams);startX = event.getRawX();startY = event.getRawY();break;case MotionEvent.ACTION_UP://判断松手时View的横坐标是靠近屏幕哪一侧,将View移动到依靠屏幕float endX = event.getRawX();float endY = event.getRawY();if (endX < getScreenWidth() / 2) {endX = 0;} else {endX = getScreenWidth() - floatBall.width;}floatBallParams.x = (int) endX;floatBall.setDragState(false);windowManager.updateViewLayout(floatBall, floatBallParams);//如果初始落点与松手落点的坐标差值超过6个像素,则拦截该点击事件//否则继续传递,将事件交给OnClickListener函数处理if (Math.abs(endX - tempX) > 6 && Math.abs(endY - tempY) > 6) {return true;}break;}return false;}};OnClickListener clickListener = new OnClickListener() {@Overridepublic void onClick(View v) {windowManager.removeView(floatBall);showFloatMenu();floatMenu.startAnimation();}};floatBall.setOnTouchListener(touchListener);floatBall.setOnClickListener(clickListener);

加速球ProgressBall的设计较为复杂,需要用到贝塞尔曲线来呈现波浪效果,且单击双击的效果也需要分开呈现
同样是让ProgressBall继承于View
进度值的意义在于限制水面最终上升到的高度,即根据目标进度值与最大进度值的比例来决定水面高度
波浪总的起伏次数Count用于在单击加速球时,水面上下波动的次数

    //view的宽度private int width = 200;//view的高度private int height = 200;//最大进度值private final int maxProgress = 100;//当前进度值private int currentProgress = 0;//目标进度值private final int targetProgress = 70;//是否为单击private boolean isSingleTop;//设定波浪总的起伏次数private final int Count = 20;//当前起伏次数private int currentCount;//初始振幅大小private final int startAmplitude = 15;//波浪周期性出现的次数private final int cycleCount = width / (startAmplitude * 4) + 1;

http://www.ppmy.cn/news/877745.html

相关文章

移动端悬浮球示例

实现思路效果图代码 注意:此例子只适用于移动端; 实现思路 1.拖动元素 2.当拖放位置在左屏时,停靠屏幕在左边,右屏时,停靠在右边 3.当元素顶部(底部)在屏幕外时,停靠在屏幕顶部(底部); 效果图 代码 <!DOCTYPE html> <html lang"en"> <head>…

【app】1.1 悬浮球_绘制

前言 测试时需要抓取QXDM log&#xff0c;但是需要连接到电脑上&#xff0c;通过adb口下diag命令&#xff0c;打算编写apk&#xff0c;运行时显示一个悬浮球&#xff0c;可以直接通过apk的service去下diag命令。 设置悬浮球是因为录制音频软件时&#xff0c;如果退出当前录音…

android悬浮球代码,Android 仿360悬浮球与加速球

先来看一张动态图 昨天跟着视频学了如何自定义View并做成仿360悬浮球与加速球的样式 可以看出来&#xff0c;做成的效果有&#xff1a; 点击按钮后退出Activity&#xff0c;呈现一个圆形的悬浮球&#xff0c;可以随意拖动并会自动依靠到屏幕一侧&#xff0c;且拖动时会变成一张…

《深度学习推荐系统》笔记

目录 一、推荐系统是什么1.作用和意义2.推荐系统的架构2.1 逻辑架构2.2 技术架构 二、传统的推荐系统方法1. 协同过滤算法1.1 userCF&&ItemCF1.3 矩阵分解算法 2. 逻辑回归算法3. 因子分解机3.1 POLY2模型3.2 FM模型3.3 FFM模型3.4 小结 4. 组合模型4.1 GBDTLR组合模型…

Qt之悬浮球菜单

一、概述 最近想做一个炫酷的悬浮式菜单&#xff0c;考虑到菜单展开和美观&#xff0c;所以考虑学习下Qt的动画系统和状态机内容&#xff0c;打开QtCreator的示例教程浏览了下&#xff0c;大致发现教程中2D Painting程序和Animated Tiles程序有所帮助&#xff0c;如下图所示&a…

【android】悬浮球

效果图 原理 获取windowManager 设定WindowManager.LayoutParams使窗口悬浮 主要涉及的值如下&#xff0c;其中 params.type WindowManager.LayoutParams.TYPE_APPLICATION; 设置悬浮窗在应用内&#xff0c;在弹出dialog会悬浮在dialog下方&#xff0c;如果将这个值设置为…

多功能悬浮球下载_悬浮球app下载

应用介绍&#xff1a; 用过flyme系统没&#xff0c;里面的悬浮球应用非常好用。以至于换了其他rom之后不太习惯&#xff0c;因此自主研发了一个高仿的版本&#xff0c;方便广大用户也一起来体验体验。该应用可以轻松实现返回键、下拉通知栏、锁屏、打开多任务、home键等实用功能…

苹果悬浮球_iPhone手机的悬浮球功能强大!你却不会用?太浪费了

阅读本文前&#xff0c;请您先点击上面的“蓝色字体”&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到文章了。每天都会有分享&#xff0c;都是免费订阅&#xff0c;请您放心关注。注图文来源网络&#xff0c;侵删 …