一个悬浮的拨打电话按钮,使用CardView+ImageView可能会出现适配问题,也就是图片显示不全,出现这种问题,就直接替换控件了,因为上述的组合控件没有FloatingActionButton使用方便,还可以有拖动和吸附效果不是更好吗。

1.一般自定义就可以实现,看看第一种方式:直接上代码了:
java
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import com.baijie.crm.activity.utils.Util;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
/**
* 悬浮吸附可拖动按钮
*/
@SuppressLint("AppCompatCustomView")
public class DragFloatActionButton extends FloatingActionButton {
private static final String TAG = "DragButton";
private int parentHeight;
private int parentWidth;
private int lastX;
private int lastY;
private boolean isDrag;
private ViewGroup parent;
public DragFloatActionButton(Context context) {
super(context);
}
public DragFloatActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@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;
this.setAlpha(0.9f);
setPressed(true);
getParent().requestDisallowInterceptTouchEvent(true);
lastX = rawX;
lastY = rawY;
if (getParent() != null) {
parent = (ViewGroup) getParent();
parentHeight = parent.getHeight();
parentWidth = parent.getWidth();
}
break;
case MotionEvent.ACTION_MOVE:
this.setAlpha(0.9f);
int dx = rawX - lastX;
int dy = rawY - lastY;
int distance = (int) Math.sqrt(dx * dx + dy * dy);
if (distance > 2 && !isDrag) {
isDrag = true;
}
float x = getX() + dx;
float y = getY() + dy;
//检测是否到达边缘 左上右下
x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
y = getY() < 0 ? 0 : getY() + getHeight() > parentHeight ? parentHeight - getHeight() : y;
setX(x);
setY(y);
lastX = rawX;
lastY = rawY;
break;
case MotionEvent.ACTION_UP:
if (isDrag) {
//恢复按压效果
setPressed(false);
moveHide(rawX);
}
break;
}
//如果是拖拽则消耗事件,否则正常传递即可。
return isDrag || super.onTouchEvent(event);
}
private void moveHide(int rawX) {
if (rawX >= parentWidth / 2) {
//靠右吸附
animate().setInterpolator(new DecelerateInterpolator())
.setDuration(500)
//.xBy(parentWidth - getWidth() - getX())
// .xBy(parentWidth - getWidth() - getX() - DensityUtils.dp2px(getContext(), 20))
.xBy(parentWidth - getWidth() - getX() - Util.dp2px(getContext(), 20))
.start();
} else {
//靠左吸附
//ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);
// 设置左侧间距 20
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(),
Util.dp2px(getContext(), 20));
oa.setInterpolator(new DecelerateInterpolator());
oa.setDuration(500);
oa.start();
}
}
}
java
public static int dp2px(Context context, float dp)
{
return (int ) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
java
<com.666.widget.DragFloatActionButton
android:id="@+id/dragFloatActionButton"
android:visibility="visible"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|center"
android:layout_marginRight="15dp"
android:layout_marginBottom="350dp"
android:src="@mipmap/sixsixsix"
app:backgroundTint="?attr/colorPrimary"
app:borderWidth="0.0dip"
app:elevation="15.0dip"
app:fabCustomSize="58dp"
app:rippleColor="#BFEFFF"/>
2.下面是之前借鉴的写法,仅供参考
java
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import com.alipay.pushsdk.util.log.LogUtil;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
/**
* 可拖拽 吸附的悬浮按钮
*/
public class AiDragFloatActionButton extends FloatingActionButton {
private int parentHeight;
private int parentWidth;
public AiDragFloatActionButton(Context context) {
super(context);
}
public AiDragFloatActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AiDragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
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:
LogUtil.d("SGF","MotionEvent.ACTION_DOWN");
setPressed(true);
isDrag = false;
getParent().requestDisallowInterceptTouchEvent(true);
lastX = rawX;
lastY = rawY;
ViewGroup parent;
if (getParent() != null) {
parent = (ViewGroup) getParent();
parentHeight = parent.getHeight();
parentWidth = parent.getWidth();
}
break;
case MotionEvent.ACTION_MOVE:
LogUtil.d("SGF","MotionEvent.ACTION_MOVE");
if (parentHeight <= 0 || parentWidth == 0) {
isDrag = false;
break;
} else {
isDrag = true;
}
int dx = rawX - lastX;
int dy = rawY - lastY;
//这里修复一些华为手机无法触发点击事件
int distance = (int) Math.sqrt(dx * dx + dy * dy);
if (distance == 0) {
isDrag = false;
break;
}
float x = getX() + dx;
float y = getY() + dy;
//检测是否到达边缘 左上右下
x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
y = getY() < 0 ? 0 : getY() + getHeight() > parentHeight ? parentHeight - getHeight() : y;
setX(x);
setY(y);
lastX = rawX;
lastY = rawY;
Log.i("aa", "isDrag=" + isDrag + "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth);
break;
case MotionEvent.ACTION_UP:
LogUtil.d("SGF","MotionEvent.ACTION_UP");
if (!isNotDrag()) {
//恢复按压效果
setPressed(false);
//Log.i("getX="+getX()+";screenWidthHalf="+screenWidthHalf);
if (rawX >= parentWidth / 2) {
//靠右吸附
animate().setInterpolator(new DecelerateInterpolator())
.setDuration(500)
// 将松开后悬停的位置修改一下(右侧保留35的间距)
.xBy((parentWidth - getWidth() - getX()) - 35)
.start();
} else {
//靠左吸附
// 将松开后悬停的位置修改一下(左侧保留35的间距)
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 35);
oa.setInterpolator(new DecelerateInterpolator());
oa.setDuration(500);
oa.start();
// animate().setInterpolator(new DecelerateInterpolator())
// .setDuration(500)
// // 将松开后悬停的位置修改一下(右侧保留35的间距)
// .xBy(50)
// .start();
}
}
break;
// case MotionEvent.ACTION_CANCEL:
// LogUtil.d("SGF","MotionEvent.ACTION_CANCEL");
// getParent().requestDisallowInterceptTouchEvent(false);
// break;
}
//如果是拖拽则消s耗事件,否则正常传递即可。
return !isNotDrag() || super.onTouchEvent(event);
}
private boolean isNotDrag() {
return !isDrag && (getX() == 0
|| (getX() == parentWidth - getWidth()));
}
}
这种方式测试功能基本是一样的,但是就是无法直接设置控件的监听事件,可以看看。
java
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
public class DraggableFloatingActionButton extends FloatingActionButton {
private int lastX, lastY;
private WindowManager.LayoutParams params;
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(new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin += dx;
layoutParams.topMargin += dy;
setLayoutParams(layoutParams);
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
}
return true;
}
});
}
}
上面这种就是最原始的自定义方式了,没有什么好描述的了。
其它相关案例: