這篇文章主要介紹Android如何實(shí)現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、網(wǎng)站制作、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、安州ssl等。為近千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的安州網(wǎng)站制作公司
懸浮按鈕FloatingActionButton是Android 5.0系統(tǒng)添加的新控件,FloatingActionButton是繼承至ImageView,所以FloatingActionButton擁有ImageView的所有屬性。本文講解的是一個(gè)實(shí)現(xiàn)了可拖拽的懸浮按鈕,并為此添加了類似于qq的吸附邊框的功能。在此之前,先了解下其簡(jiǎn)單的使用方式吧:
首先你得添加其依賴
compile 'com.android.support:design:25.3.1'
然后在布局文件中使用。
如圖:
FloatingActionButton正常顯示的情況下有個(gè)填充的顏色,有個(gè)陰影;點(diǎn)擊的時(shí)候會(huì)有一個(gè)rippleColor,并且陰影的范圍可以增大。其中:
1、填充的顏色默認(rèn)使用就是style當(dāng)中的colorAccent。
2、rippleColor默認(rèn)取的是Theme當(dāng)中的colorControlHighlight。
3、elevation和pressedTranslationZ,前者用戶設(shè)置正常顯示的陰影大小;后者是點(diǎn)擊時(shí)顯示的陰影大小。
好了,現(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ì)算手指移動(dòng)了多少 int dx = rawX - lastX; int dy = rawY - lastY; //這里修復(fù)一些手機(jī)無(wú)法觸發(fā)點(diǎn)擊事件的問題 int distance= (int) Math.sqrt(dx*dx+dy*dy); Log.e("distance---->",distance+""); if(distance<3){//給個(gè)容錯(cuò)范圍,不然有部分手機(jī)還是無(wú)法點(diǎn)擊 isDrag=false; break; } float x = getX() + dx; float y = getY() + dy; //檢測(cè)是否到達(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; } }
上面的代碼也很簡(jiǎn)單,相信看代碼中的注釋就可以看的明白了。但是這里還是講下其實(shí)現(xiàn)原理:這個(gè)自定義的懸浮按鈕,我們主要是重寫了其onTouch事件,捕捉觸摸事件,然后利用setX(),setY()方法將其移動(dòng)。而吸附效果,主要是利用的屬性動(dòng)畫,最后,不要忘了return 是否還在拖拽的結(jié)果,免得無(wú)法觸發(fā)點(diǎn)擊事件。
PS
最后貼一個(gè)彈出框。推薦用popmenu,相比于popwindow,這個(gè)會(huì)自動(dòng)調(diào)整顯示的位置,這在拖拽的懸浮按鈕中很有用,因?yàn)槿绻煤笳?,你將按鈕移到屏幕上方,而當(dāng)你的彈出框也是設(shè)置在顯示的懸浮按鈕的上方,那么就有可能會(huì)遮擋彈出框的內(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文件
以上是“Android如何實(shí)現(xiàn)自定義可拖拽的懸浮按鈕DragFloatingActionButton”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!