日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 自定义View:悬浮球与加速球 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先來看一張動態圖

昨天跟著視頻學了如何自定義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;

總結

以上是生活随笔為你收集整理的自定义View:悬浮球与加速球的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。