溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android如何實現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton

發(fā)布時間:2021-06-30 10:58:53 來源:億速云 閱讀:232 作者:小新 欄目:移動開發(fā)

這篇文章主要介紹Android如何實現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

懸浮按鈕FloatingActionButton是Android 5.0系統(tǒng)添加的新控件,FloatingActionButton是繼承至ImageView,所以FloatingActionButton擁有ImageView的所有屬性。本文講解的是一個實現(xiàn)了可拖拽的懸浮按鈕,并為此添加了類似于qq的吸附邊框的功能。在此之前,先了解下其簡單的使用方式吧:

首先你得添加其依賴

compile 'com.android.support:design:25.3.1'

然后在布局文件中使用。

<android.support.design.widget.FloatingActionButton
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="right|bottom"
   android:src="@drawable/ic_launcher"
   />

如圖:

Android如何實現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton 

FloatingActionButton正常顯示的情況下有個填充的顏色,有個陰影;點擊的時候會有一個rippleColor,并且陰影的范圍可以增大。其中:

1、填充的顏色默認(rèn)使用就是style當(dāng)中的colorAccent。

2、rippleColor默認(rèn)取的是Theme當(dāng)中的colorControlHighlight。

3、elevation和pressedTranslationZ,前者用戶設(shè)置正常顯示的陰影大小;后者是點擊時顯示的陰影大小。

好了,現(xiàn)在介紹本文的重點:可拖拽的,有吸附功能的懸浮按鈕

先上代碼。

import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
public class DragFloatActionButton extends FloatingActionButton {
 private int screenWidth;
 private int screenHeight;
 private int screenWidthHalf;
 private int statusHeight;
 private int virtualHeight;
 public DragFloatActionButton(Context context) {
  super(context);
  init();
 }
 public DragFloatActionButton(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }
 public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 private void init() {
  screenWidth = ScreenUtils.getScreenWidth(getContext());
  screenWidthHalf = screenWidth / 2;
  screenHeight = ScreenUtils.getScreenHeight(getContext());
  statusHeight = ScreenUtils.getStatusHeight(getContext());
  virtualHeight=ScreenUtils.getVirtualBarHeigh(getContext());
 }
 private int lastX;
 private int lastY;
 private boolean isDrag;
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  int rawX = (int) event.getRawX();
  int rawY = (int) event.getRawY();
  switch (event.getAction() & MotionEvent.ACTION_MASK) {
   case MotionEvent.ACTION_DOWN:
    isDrag = false;
    getParent().requestDisallowInterceptTouchEvent(true);
    lastX = rawX;
    lastY = rawY;
    Log.e("down---->", "getX=" + getX() + ";screenWidthHalf=" + screenWidthHalf);
    break;
   case MotionEvent.ACTION_MOVE:
    isDrag = true;
    //計算手指移動了多少
    int dx = rawX - lastX;
    int dy = rawY - lastY;
    //這里修復(fù)一些手機無法觸發(fā)點擊事件的問題
    int distance= (int) Math.sqrt(dx*dx+dy*dy);
    Log.e("distance---->",distance+"");
    if(distance<3){//給個容錯范圍,不然有部分手機還是無法點擊
     isDrag=false;
     break;
    }
    float x = getX() + dx;
    float y = getY() + dy;
    //檢測是否到達(dá)邊緣 左上右下
    x = x < 0 ? 0 : x > screenWidth - getWidth() ? screenWidth - getWidth() : x;
    // y = y < statusHeight ? statusHeight : (y + getHeight() >= screenHeight ? screenHeight - getHeight() : y);
    if (y<0){
     y=0;
    }
    if (y>screenHeight-statusHeight-getHeight()){
     y=screenHeight-statusHeight-getHeight();
    }
    setX(x);
    setY(y);
    lastX = rawX;
    lastY = rawY;
    Log.e("move---->", "getX=" + getX() + ";screenWidthHalf=" + screenWidthHalf + " " + isDrag+" statusHeight="+statusHeight+ " virtualHeight"+virtualHeight+ " screenHeight"+ screenHeight+" getHeight="+getHeight()+" y"+y);
    break;
   case MotionEvent.ACTION_UP:
    if (isDrag) {
     //恢復(fù)按壓效果
     setPressed(false);
     Log.e("ACTION_UP---->", "getX=" + getX() + ";screenWidthHalf=" + screenWidthHalf);
     if (rawX >= screenWidthHalf) {
      animate().setInterpolator(new DecelerateInterpolator())
        .setDuration(500)
        .xBy(screenWidth - getWidth() - getX())
        .start();
     } else {
      ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);
      oa.setInterpolator(new DecelerateInterpolator());
      oa.setDuration(500);
      oa.start();
     }
    }
    Log.e("up---->",isDrag+"");
    break;
  }
  //如果是拖拽則消耗事件,否則正常傳遞即可。
  return isDrag || super.onTouchEvent(event);
 }
}

ScreenUtils.Java

package com.example.cmos.retrofitdemo;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import java.lang.reflect.Method;
/**
 * Created by gongwq on 2017/6/14 0014.
 */
public class ScreenUtils {
 private ScreenUtils() {
  /* cannot be instantiated */
  throw new UnsupportedOperationException("cannot be instantiated");
 }
 /**
  * 獲得屏幕高度
  *
  * @param context
  * @return
  */
 public static int getScreenWidth(Context context) {
  WindowManager wm = (WindowManager) context
    .getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics outMetrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(outMetrics);
  return outMetrics.widthPixels;
 }
 /**
  * 獲得屏幕寬度
  *
  * @param context
  * @return
  */
 public static int getScreenHeight(Context context) {
  WindowManager wm = (WindowManager) context
    .getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics outMetrics = new DisplayMetrics();
  wm.getDefaultDisplay().getMetrics(outMetrics);
  return outMetrics.heightPixels;
 }
 /**
  * 獲得狀態(tài)欄的高度
  *
  * @param context
  * @return
  */
 public static int getStatusHeight(Context context) {
  int statusHeight = -1;
  try {
   Class<?> clazz = Class.forName("com.android.internal.R$dimen");
   Object object = clazz.newInstance();
   int height = Integer.parseInt(clazz.getField("status_bar_height")
     .get(object).toString());
   statusHeight = context.getResources().getDimensionPixelSize(height);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return statusHeight;
 }
 /**
  * 獲取虛擬功能鍵高度
  */
 public static int getVirtualBarHeigh(Context context) {
  int vh = 0;
  WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  Display display = windowManager.getDefaultDisplay();
  DisplayMetrics dm = new DisplayMetrics();
  try {
   @SuppressWarnings("rawtypes")
   Class c = Class.forName("android.view.Display");
   @SuppressWarnings("unchecked")
   Method method = c.getMethod("getRealMetrics", DisplayMetrics.class);
   method.invoke(display, dm);
   vh = dm.heightPixels - windowManager.getDefaultDisplay().getHeight();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return vh;
 }
 public static int getVirtualBarHeigh(Activity activity) {
  int titleHeight = 0;
  Rect frame = new Rect();
  activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
  int statusHeight = frame.top;
  titleHeight = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop() - statusHeight;
  return titleHeight;
 }
}

上面的代碼也很簡單,相信看代碼中的注釋就可以看的明白了。但是這里還是講下其實現(xiàn)原理:這個自定義的懸浮按鈕,我們主要是重寫了其onTouch事件,捕捉觸摸事件,然后利用setX(),setY()方法將其移動。而吸附效果,主要是利用的屬性動畫,最后,不要忘了return 是否還在拖拽的結(jié)果,免得無法觸發(fā)點擊事件。

PS

最后貼一個彈出框。推薦用popmenu,相比于popwindow,這個會自動調(diào)整顯示的位置,這在拖拽的懸浮按鈕中很有用,因為如果用后者,你將按鈕移到屏幕上方,而當(dāng)你的彈出框也是設(shè)置在顯示的懸浮按鈕的上方,那么就有可能會遮擋彈出框的內(nèi)容。

dragFloatActionButton= (DragFloatActionButton) findViewById(R.id.floatBtn);
  dragFloatActionButton.setOnClickListener(this);
....
 @Override
 public void onClick(View view) {
  switch (view.getId()) {
   case R.id.floatBtn:
    PopupMenu popupMenu=new PopupMenu(this,view);
    getMenuInflater().inflate(R.menu.pop_item,popupMenu.getMenu());
    popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
     @Override
     public boolean onMenuItemClick(MenuItem menuItem) {
      switch (menuItem.getItemId()){
       case R.id.action_last:
        Toast.makeText(TestActivity.this,""+menuItem.getItemId(),Toast.LENGTH_SHORT).show();
        break;
       case R.id.action_next:
        Toast.makeText(TestActivity.this,""+menuItem.getItemId(),Toast.LENGTH_SHORT).show();
        break;
      }
      return false;
     }
    });
    popupMenu.show();
    Log.e("****--->","float");
    // Toast.makeText(this,"flaot---",Toast.LENGTH_SHORT).show();
    break;
  }
 }

     新建menu文件夾,在里面添加pop_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 <item
  android:id="@+id/action_delete"
  android:orderInCategory="100"
  android:title="刪除"
  app:showAsAction="never" />
 <item
  android:id="@+id/action_save"
  android:orderInCategory="200"
  android:title="保存"
  app:showAsAction="never" />
 <item
  android:id="@+id/action_last"
  android:orderInCategory="300"
  android:title="上一步"
  app:showAsAction="never" />
 <item
  android:id="@+id/action_next"
  android:icon="@null"
  android:orderInCategory="400"
  android:title="下一步"
  app:showAsAction="never" />
</menu>

以上是“Android如何實現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI