在自己的项目中实现的,没有做什么封装和通用性的使用,可用就行了。实现的原理就是采用一个通用的布局,利用ActivityLifecycle监听Activity的生命周期动态的添加和删除浮动按钮。
FloatButton
参考了一位大神的代码,忘了源码地址了。
package com.newbox.jnitest;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
public abstract class FloatButton extends RelativeLayout implements View.OnClickListener {
private int parentHeight;//悬浮的父布局高度
private int parentWidth;
private FrameLayout flRoot;
public FloatButton(Context context) {
this(context, null, 0);
}
public FloatButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
View view = LayoutInflater.from(context).inflate(R.layout.float_button, this);
renderView(view);
}
public void show(Activity activity, FrameLayout.LayoutParams layoutParams) {
flRoot = (FrameLayout) activity.getWindow().getDecorView();
this.setId(R.id.float_button);
flRoot.addView(this, layoutParams);
}
public void dismiss() {
if (flRoot != null) {
flRoot.removeView(this);
}
}
public void renderView(View view) {
view.setOnClickListener(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
View view = getChildAt(0);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
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:
setPressed(true);//默认是点击事件
isDrag = false;//默认是非拖动而是点击事件
getParent().requestDisallowInterceptTouchEvent(true);//父布局不要拦截子布局的监听
BaseApplication.lastX = rawX;
BaseApplication.lastY = rawY;
ViewGroup parent;
if (getParent() != null) {
parent = (ViewGroup) getParent();
parentHeight = parent.getHeight();
parentWidth = parent.getWidth();
}
break;
case MotionEvent.ACTION_MOVE:
isDrag = (parentHeight > 0 && parentWidth > 0);//只有父布局存在你才可以拖动
if (!isDrag) break;
int dx = rawX - BaseApplication.lastX;
int dy = rawY - BaseApplication.lastY;
//这里修复一些华为手机无法触发点击事件
int distance = (int) Math.sqrt(dx * dx + dy * dy);
isDrag = distance > 0;//只有位移大于0说明拖动了
if (!isDrag) break;
float x = getX() + dx;
float y = getY() + dy;
//检测是否到达边缘 左上右下
x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
y = y < 0 ? 0 : y > parentHeight - getHeight() ? parentHeight - getHeight() : y;
setX(x);
setY(y);
BaseApplication.lastX = rawX;
BaseApplication.lastY = rawY;
break;
case MotionEvent.ACTION_UP:
//如果是拖动状态下即非点击按压事件
setPressed(!isDrag);
break;
}
//如果不是拖拽,那么就不消费这个事件,以免影响点击事件的处理
//拖拽事件要自己消费
return isDrag || super.onTouchEvent(event);
}
}
BaseApplication里面主要代码
@Override
public void onCreate() {
super.onCreate();
initLifeCycle();
}
public static int lastX;
public static int lastY;
private WeakReference wrf;
private FrameLayout.LayoutParams layoutParams;
private void initLifeCycle() {
DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
lastX = displayMetrics.widthPixels - 80;
lastY = displayMetrics.heightPixels * 2 / 3;
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(final Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(final Activity activity) {
FloatButton button;
if (wrf == null || wrf.get() == null) {
button = new FloatButton(getApplicationContext()) {
@Override
public void onClick(View v) {
activity.startActivity(new Intent(activity, SecondActivity.class));
// Intent intent = new Intent();
// intent.setAction("android.intent.action.VIEW");
// Uri content_url = Uri.parse("http://www.cnblogs.com");
// intent.setData(content_url);
// activity.startActivity(intent);
}
};
wrf = new WeakReference<>(button);
} else {
button = wrf.get();
}
if (layoutParams == null) {
layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.topMargin = BaseApplication.lastY;
layoutParams.leftMargin = BaseApplication.lastX;
layoutParams.width = 80;
layoutParams.height = 80;
}
button.show(activity, layoutParams);
}
@Override
public void onActivityPaused(Activity activity) {
FloatButton button = activity.findViewById(R.id.float_button);
if (button != null) {
button.dismiss();
}
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
基本实现了效果,如果需要在某些界面不显示的话,在onActivityResumed、onActivityPaused做过滤处理。
目前已知的Bug是,有小概率的在某些界面回到了默认的位置。