我在我的应用中使用了FloatingActionButton。偶尔,它会重叠基本内容,所以我想这样做,这样用户就可以将FAB拖出去了。
本身不需要拖放功能。它只需要是可移动的。文档没有提到这一点,但我确信我已经在其他应用程序中看到了这样的功能。
你能否建议/提供一个代码片段(最好用XML格式)。
答案 0 :(得分:37)
所以,你想创建一个Movable FloatingActionButton
,呵呵?!
基于this answer for another SO question这是我创建的代码。它似乎运行良好(具有工作点击功能)并且不依赖于FAB的父布局或定位......
package com.example;
import android.content.Context;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
public class MovableFloatingActionButton extends FloatingActionButton implements View.OnTouchListener {
private final static float CLICK_DRAG_TOLERANCE = 10; // Often, there will be a slight, unintentional, drag when the user taps the FAB, so we need to account for this.
private float downRawX, downRawY;
private float dX, dY;
public MovableFloatingActionButton(Context context) {
super(context);
init();
}
public MovableFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MovableFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOnTouchListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent){
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)view.getLayoutParams();
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
downRawX = motionEvent.getRawX();
downRawY = motionEvent.getRawY();
dX = view.getX() - downRawX;
dY = view.getY() - downRawY;
return true; // Consumed
}
else if (action == MotionEvent.ACTION_MOVE) {
int viewWidth = view.getWidth();
int viewHeight = view.getHeight();
View viewParent = (View)view.getParent();
int parentWidth = viewParent.getWidth();
int parentHeight = viewParent.getHeight();
float newX = motionEvent.getRawX() + dX;
newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent
newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent
float newY = motionEvent.getRawY() + dY;
newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent
newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent
view.animate()
.x(newX)
.y(newY)
.setDuration(0)
.start();
return true; // Consumed
}
else if (action == MotionEvent.ACTION_UP) {
float upRawX = motionEvent.getRawX();
float upRawY = motionEvent.getRawY();
float upDX = upRawX - downRawX;
float upDY = upRawY - downRawY;
if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click
return performClick();
}
else { // A drag
return true; // Consumed
}
}
else {
return super.onTouchEvent(motionEvent);
}
}
}
这是XML ......
<com.example.MovableFloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_navigate_next_white_24dp"/>
基本上,您只需要在XML中将android.support.design.widget.FloatingActionButton
替换为com.example.MovableFloatingActionButton
。
答案 1 :(得分:0)
您只需在onTouch
{/ 1>}上实施View
即可尝试以下方式
<强> XML 强>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/rootlayout"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
<强>的java 强>
public class dragativity extends AppCompatActivity implements View.OnTouchListener{
FloatingActionButton fab;
FrameLayout rootlayout;
int _xDelta;
int _yDelta;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drag);
rootlayout = (FrameLayout) findViewById(R.id.rootlayout);
fab = (FloatingActionButton) findViewById(R.id.fab);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(150, 150);
fab.setLayoutParams(layoutParams);
fab.setOnTouchListener(dragativity.this);
}
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view
.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
rootlayout.invalidate();
return true;
}
}
答案 2 :(得分:0)
尝试一下:
public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
float dX;
float dY;
int lastAction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View dragView = findViewById(R.id.draggable_view);
dragView.setOnTouchListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
lastAction = MotionEvent.ACTION_DOWN;
break;
case MotionEvent.ACTION_MOVE:
view.setY(event.getRawY() + dY);
view.setX(event.getRawX() + dX);
lastAction = MotionEvent.ACTION_MOVE;
break;
case MotionEvent.ACTION_UP:
if (lastAction == MotionEvent.ACTION_DOWN)
Toast.makeText(DraggableView.this, "Clicked!", Toast.LENGTH_SHORT).show();
break;
default:
return false;
}
return true;
}
}
和XML:
<ImageButton
android:id="@+id/draggable_view"
android:background="@mipmap/ic_launcher"
android:layout_gravity="bottom|right"
android:layout_marginBottom="20dp"
android:layout_marginEnd="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
您可以将任意视图设为可拖动和可点击。
答案 3 :(得分:0)
实际上,您可以只使用android.support.design.widget.CoordinatorLayout而不是Relative布局或任何其他布局,这将起作用(移动FAB)
答案 4 :(得分:0)
基于@ ban-geoengineering答案,我更新为执行涟漪效应和左右重力,如faceebook聊天气泡。如果在此代码块内消耗了触摸事件,我就创建了自定义的点击侦听器cuz,它的波纹效果无法清晰显示。
<com.sample.DraggableFloatingActionButton
android:id="@+id/connect_to_support_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginLeft="@dimen/spacing_10pt"
android:layout_marginRight="@dimen/spacing_10pt"
android:layout_marginBottom="@dimen/spacing_16pt"
android:clickable="true"
android:focusable="true"
app:backgroundTint="@color/colorGreen"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:rippleColor="@color/colorWhite"
app:srcCompat="@drawable/ic_live_support"
app:tint="@color/colorWhite" />
package com.sample;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class DraggableFloatingActionButton extends FloatingActionButton implements View.OnTouchListener {
CustomClickListener customClickListener;
private final static float CLICK_DRAG_TOLERANCE = 10; // Often, there will be a slight, unintentional, drag when the user taps the FAB, so we need to account for this.
private float downRawX, downRawY;
private float dX, dY;
int viewWidth;
int viewHeight;
int parentWidth;
int parentHeight;
float newX;
float newY;
public DraggableFloatingActionButton(Context context) {
super(context);
init();
}
public DraggableFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DraggableFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOnTouchListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
downRawX = motionEvent.getRawX();
downRawY = motionEvent.getRawY();
dX = view.getX() - downRawX;
dY = view.getY() - downRawY;
return false; // not Consumed for ripple effect
} else if (action == MotionEvent.ACTION_MOVE) {
viewWidth = view.getWidth();
viewHeight = view.getHeight();
View viewParent = (View) view.getParent();
parentWidth = viewParent.getWidth();
parentHeight = viewParent.getHeight();
newX = motionEvent.getRawX() + dX;
newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent
newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent
newY = motionEvent.getRawY() + dY;
newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent
newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent
view.animate()
.x(newX)
.y(newY)
.setDuration(0)
.start();
return true; // Consumed
} else if (action == MotionEvent.ACTION_UP) {
float upRawX = motionEvent.getRawX();
float upRawY = motionEvent.getRawY();
float upDX = upRawX - downRawX;
float upDY = upRawY - downRawY;
if (newX > ((parentWidth - viewWidth - layoutParams.rightMargin) / 2)) {
newX = parentWidth - viewWidth - layoutParams.rightMargin;
} else {
newX = layoutParams.leftMargin;
}
view.animate()
.x(newX)
.y(newY)
.setInterpolator(new OvershootInterpolator())
.setDuration(300)
.start();
if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click
if (customClickListener != null) {
customClickListener.onClick(view);
}
return false;// not Consumed for ripple effect
} else { // A drag
return false; // not Consumed for ripple effect
}
} else {
return super.onTouchEvent(motionEvent);
}
}
public void setCustomClickListener(CustomClickListener customClickListener) {
this.customClickListener = customClickListener;
}
public interface CustomClickListener {
void onClick(View view);
}
}
答案 5 :(得分:0)
所有建议的答案都使用OnTouch侦听器,由于可访问性实现,最近的Android API不建议使用。还要注意,startDrag()方法已过时。开发人员应改用startDragAndDrop()。 我的实现使用OnDragListener()如下:
将以下代码段放入onCreatView()方法中,其中 root 是充气机拍摄的根视图(或其他任何可以接收Drop事件的视图);
final FloatingActionButton fab = root.findViewById(R.id.my_fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Do whatever this button will do on click event
}
});
root.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_LOCATION:
dX = event.getX();
dY = event.getY();
break;
case DragEvent.ACTION_DRAG_ENDED:
fab.setX(dX-fab.getWidth()/2);
fab.setY(dY-fab.getHeight()/2);
break;
}
return true;
}
});
fab.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
View.DragShadowBuilder myShadow = new View.DragShadowBuilder(fab);
v.startDragAndDrop(null, myShadow, null, View.DRAG_FLAG_GLOBAL);
return true;
}
});
答案 6 :(得分:0)
这是为我工作的听众,公差为70。
private class FloatingOnTouchListener implements View.OnTouchListener {
private float x;
private float y;
private float nowX;
private float nowY;
private float downX;
private float downY;
private final int tolerance = 70;
@Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) event.getRawX();
y = (int) event.getRawY();
downX = x;
downY = y;
} else
if (event.getAction() == MotionEvent.ACTION_MOVE) {
nowX = event.getRawX();
nowY = event.getRawY();
float movedX = nowX - x;
float movedY = nowY - y;
x = nowX;
y = nowY;
iconViewLayoutParams.x = iconViewLayoutParams.x + (int) movedX;
iconViewLayoutParams.y = iconViewLayoutParams.y + (int) movedY;
windowManager.updateViewLayout(view, iconViewLayoutParams);
} else
if (event.getAction() == MotionEvent.ACTION_UP) {
float dx = Math.abs(nowX - downX);
float dy = Math.abs(nowY - downY);
if (dx < tolerance && dy < tolerance) {
Log.d(TAG, "clicou");
Log.d(TAG, "dx " + dx);
Log.d(TAG, "dy " + dy);
windowManager.removeViewImmediate(iconView);
windowManager.addView(displayView, layoutParams);
} else {
Log.d(TAG, "dx " + dx);
Log.d(TAG, "dy " + dy);
return true;
}
}
return true;
}
}
答案 7 :(得分:-1)
您可以尝试此代码 XML
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:id="@+id/dashboardShowActionsFab"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
JAVA
fab.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
downRawX = motionEvent.getRawX();
downRawY = motionEvent.getRawY();
dX = view.getX() - downRawX;
dY = view.getY() - downRawY;
return true; // Consumed
} else if (action == MotionEvent.ACTION_MOVE) {
int viewWidth = view.getWidth();
int viewHeight = view.getHeight();
View viewParent = (View) view.getParent();
int parentWidth = viewParent.getWidth();
int parentHeight = viewParent.getHeight();
float newX = motionEvent.getRawX() + dX;
newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent
newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent
float newY = motionEvent.getRawY() + dY;
newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent
newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent
view.animate()
.x(newX)
.y(newY)
.setDuration(0)
.start();
return true; // Consumed
} else if (action == MotionEvent.ACTION_UP) {
float upRawX = motionEvent.getRawX();
float upRawY = motionEvent.getRawY();
float upDX = upRawX - downRawX;
float upDY = upRawY - downRawY;
if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click
// return performClick();
Toast.makeText(MainActivity.this, "clicked", Toast.LENGTH_SHORT).show();
} else { // A drag
return true; // Consumed
}
} else {
//return super.onTouchEvent(motionEvent);
}
return true;
}
答案 8 :(得分:-1)
这里是一个稍微更新的版本。它可以正确处理涟漪效应,至少对我有用。
public MovableFloatingActionButton(Context context) {
super(context);
init();
}
public MovableFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MovableFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOnTouchListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent){
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)view.getLayoutParams();
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
downRawX = motionEvent.getRawX();
downRawY = motionEvent.getRawY();
dX = view.getX() - downRawX;
dY = view.getY() - downRawY;
return super.onTouchEvent(motionEvent);
case MotionEvent.ACTION_MOVE:
int viewWidth = view.getWidth();
int viewHeight = view.getHeight();
View viewParent = (View)view.getParent();
int parentWidth = viewParent.getWidth();
int parentHeight = viewParent.getHeight();
float newX = motionEvent.getRawX() + dX;
newX = Math.max(layoutParams.leftMargin, newX);
newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX);
float newY = motionEvent.getRawY() + dY;
newY = Math.max(layoutParams.topMargin, newY);
newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY);
view.animate().x(newX).y(newY).setDuration(0).start();
setPressed(false);
return true;
case MotionEvent.ACTION_UP:
final float upRawX = motionEvent.getRawX();
final float upRawY = motionEvent.getRawY();
final float upDX = upRawX - downRawX;
final float upDY = upRawY - downRawY;
final boolean isDrag = Math.abs(upDX) >= CLICK_DRAG_TOLERANCE || Math.abs(upDY) >= CLICK_DRAG_TOLERANCE;
return isDrag || performClick();
default:
return super.onTouchEvent(motionEvent);
}
}