自定义View:悬浮球与加速球
先來看一張動態圖
昨天跟著視頻學了如何自定義View并做成仿360懸浮球與加速球的樣式
可以看出來,做成的效果有:
- 點擊按鈕后退出Activity,呈現一個圓形的懸浮球,可以隨意拖動并會自動依靠到屏幕一側,且拖動時會變成一張圖片
- 當點擊懸浮球時,懸浮球隱藏,底部出現一個加速球,雙擊加速球時,呈現水量逐漸增高且波動幅度較小的效果,單擊時波浪上下波動且幅度漸小
- 點擊屏幕不包含底部加速球的部位,加速球會隱藏,懸浮球重新出現
要做出這么一個效果,需要兩個自定義View與一個自定義ViewGroup
首先,需要先設計懸浮球View——FloatBall
簡單起見,為FloatBall指定一個默認寬度和高度——150像素
然后在onDraw(Canvas canvas)方法中,判斷FloatBall是否正在被拖動isDrag,如果是,則繪制一張默認圖片bitmap,否則則根據繪圖函數繪制圓形與居中文本
因為FloatBall是不存在于Activity中而在屏幕單獨顯示的,所以需要用WindowManager來添加View并顯示
新建一個類,命名為ViewManager,用來總的管理View的顯示與刪除
私有化構造函數并采用單例模式
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()
此時,只要為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:悬浮球与加速球的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员需要了解的十个高级SQL概念
- 下一篇: 分页的limit_分页场景(limit,