Android自定義可拖拽的懸浮按鈕DragFloatingActionButton
懸浮按鈕FloatingActionButton是Android 5.0系統(tǒng)添加的新控件,FloatingActionButton是繼承至ImageView,所以FloatingActionButton擁有ImageView的所有屬性。本文講解的是一個實(shí)現(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" />
如圖:
FloatingActionButton正常顯示的情況下有個填充的顏色,有個陰影;點(diǎn)擊的時候會有一個rippleColor,并且陰影的范圍可以增大。其中:
1、填充的顏色默認(rèn)使用就是style當(dāng)中的colorAccent。
2、rippleColor默認(rèn)取的是Theme當(dāng)中的colorControlHighlight。
3、elevation和pressedTranslationZ,前者用戶設(shè)置正常顯示的陰影大?。缓笳呤屈c(diǎn)擊時顯示的陰影大小。
好了,現(xiàn)在介紹本文的重點(diǎ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;
//計(jì)算手指移動了多少
int dx = rawX - lastX;
int dy = rawY - lastY;
//這里修復(fù)一些手機(jī)無法觸發(fā)點(diǎn)擊事件的問題
int distance= (int) Math.sqrt(dx*dx+dy*dy);
Log.e("distance---->",distance+"");
if(distance<3){//給個容錯范圍,不然有部分手機(jī)還是無法點(diǎn)擊
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;
}
}
上面的代碼也很簡單,相信看代碼中的注釋就可以看的明白了。但是這里還是講下其實(shí)現(xiàn)原理:這個自定義的懸浮按鈕,我們主要是重寫了其onTouch事件,捕捉觸摸事件,然后利用setX(),setY()方法將其移動。而吸附效果,主要是利用的屬性動畫,最后,不要忘了return 是否還在拖拽的結(jié)果,免得無法觸發(fā)點(diǎn)擊事件。
PS
最后貼一個彈出框。推薦用popmenu,相比于popwindow,這個會自動調(diào)整顯示的位置,這在拖拽的懸浮按鈕中很有用,因?yàn)槿绻煤笳?,你將按鈕移到屏幕上方,而當(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自定義可拖拽的懸浮按鈕DragFloatingActionButton,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android開發(fā)實(shí)現(xiàn)自定義新聞加載頁面功能實(shí)例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)自定義新聞加載頁面功能,結(jié)合具體實(shí)例形式分析了Android界面加載及頁面布局相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
Android自定義實(shí)現(xiàn)循環(huán)滾輪控件WheelView
滾輪布局WheelView大家經(jīng)常使用,比如在選擇生日的時候,風(fēng)格類似系統(tǒng)提供的DatePickerDialog,這篇文章主要為大家詳細(xì)介紹了Android自定義實(shí)現(xiàn)循環(huán)滾輪控件WheelView,感興趣的小伙伴們可以參考一下2016-07-07
Android 判斷所有字段是否已經(jīng)輸入的實(shí)例
今天小編就為大家分享一篇Android 判斷所有字段是否已經(jīng)輸入的實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android中Volley框架進(jìn)行請求網(wǎng)絡(luò)數(shù)據(jù)的使用
這篇文章主要介紹了Android中Volley框架進(jìn)行請求網(wǎng)絡(luò)數(shù)據(jù)的使用,本文給大家介紹的非常詳細(xì)具有參考借鑒價值,需要的朋友可以參考下2016-10-10
Android自定義view實(shí)現(xiàn)拖動小球移動
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)拖動小球移動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-11-11
如何使用Matrix對bitmap的旋轉(zhuǎn)與鏡像水平垂直翻轉(zhuǎn)
本篇文章是對使用Matrix對bitmap的旋轉(zhuǎn)與鏡像水平垂直翻轉(zhuǎn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android 通過自定義view實(shí)現(xiàn)水波紋效果案例詳解
這篇文章主要介紹了Android 通過自定義view實(shí)現(xiàn)水波紋效果案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Android簡單記錄和恢復(fù)ListView滾動位置的方法
這篇文章主要介紹了Android簡單記錄和恢復(fù)ListView滾動位置的方法,涉及Android針對ListView位置屬性的相關(guān)操作技巧,需要的朋友可以參考下2016-08-08

