Android实现自由拖动并显示文字的悬浮框

作者:化作孤岛的瓜 时间:2023-06-08 06:30:39 

项目中需要实现一个状态显示的悬浮框,要求可以设置两种模式:拖动模式和不可拖动模式。

实现效果图如下:

Android实现自由拖动并显示文字的悬浮框

实现步骤:

1.首先要设置该悬浮框的基本属性:


/**
* 显示弹出框
*
* @param context
*/
@SuppressWarnings("WrongConstant")
public static void showPopupWindow(final Context context, String showtxt) {
if (isShown) {
 return;
}
isShown = true;
// 获取WindowManager
mWindowManager = (WindowManager) context
 .getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context, showtxt);

params = new WindowManager.LayoutParams();
// 类型,系统提示以及它总是出现在应用程序窗口之上。
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;

// 设置flag
int flags = canTouchFlags;

// | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
params.flags = flags;
// 不设置这个弹出框的透明遮罩显示为黑色
params.format = PixelFormat.TRANSLUCENT;
// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
// 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
// 不设置这个flag的话,home页的划屏会有问题
params.width = LayoutParams.WRAP_CONTENT;
params.height = LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.TOP;
mWindowManager.addView(mView, params);
}

比较重要的点是要注意设置flags,我这里提供了两种flags以供切换:


private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

第一种是可触摸不可聚焦模式,第二种是不可触摸不可聚焦模式。其他的flags可以从api中查阅。

2.设置悬浮框的拖动监听事件:


private static View setUpView(final Context context, String showtxt) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow,
 null);

TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop);
showTv.setText(showtxt);

rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop);
rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() {
 private float lastX; //上一次位置的X.Y坐标
 private float lastY;
 private float nowX; //当前移动位置的X.Y坐标
 private float nowY;
 private float tranX; //悬浮窗移动位置的相对值
 private float tranY;

@Override
 public boolean onTouch(View v, MotionEvent event) {
 boolean ret = false;
 switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:

// 获取按下时的X,Y坐标
  lastX = event.getRawX();
  lastY = event.getRawY();
  ret = true;

break;
  case MotionEvent.ACTION_MOVE:
  // 获取移动时的X,Y坐标
  nowX = event.getRawX();
  nowY = event.getRawY();
  // 计算XY坐标偏移量
  tranX = nowX - lastX;
  tranY = nowY - lastY;
  params.x += tranX;
  params.y += tranY;

//更新悬浮窗位置
  mWindowManager.updateViewLayout(mView, params);
  //记录当前坐标作为下一次计算的上一次移动的位置坐标
  lastX = nowX;
  lastY = nowY;

break;
  case MotionEvent.ACTION_UP:
  break;
 }
 return ret;
 }
});

这里要在down的时候记录坐标,move事件中使用修改params坐标进行移动。

3.设置悬浮框文字属性:


public static void setShowTxt(String txt) {
try {
 TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);
 showTv.setText(txt);
 mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
 Log.d(TAG, "setShowTxt: 更新悬浮框错误");
 e.printStackTrace();
 if(e.getMessage().contains("not attached to window manager")){
 mWindowManager.addView(mView, params);
 }
}
}

4.更新悬浮框图片显示:


public static void setShowImg(Bitmap bitmap) {
try {
 ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);
 showImg.setImageBitmap(bitmap);
 mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
 Log.d(TAG, "setShowTxt: 更新悬浮框错误");
 e.printStackTrace();
 if(e.getMessage().contains("not attached to window manager")){
 mWindowManager.addView(mView, params);
 }
}
}

介绍完毕,整个类都封装好了,代码如下:


/**
* 悬浮窗工具类
* created by Pumpkin at 17/3/28
*/
public class WindowsUitlity {
private static String TAG = WindowsUitlity.class.getSimpleName();
private static WindowManager mWindowManager = null;
private static WindowManager.LayoutParams params;
public static Boolean isShown = false;
private static View mView = null;

/**
* 显示弹出框
*
* @param context
*/
@SuppressWarnings("WrongConstant")
public static void showPopupWindow(final Context context, String showtxt) {
if (isShown) {
 return;
}
isShown = true;
// 获取WindowManager
mWindowManager = (WindowManager) context
 .getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context, showtxt);

params = new WindowManager.LayoutParams();
// 类型,系统提示以及它总是出现在应用程序窗口之上。
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
 WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;

// 设置flag
int flags = canTouchFlags;

// | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件
params.flags = flags;
// 不设置这个弹出框的透明遮罩显示为黑色
params.format = PixelFormat.TRANSLUCENT;
// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
// 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按
// 不设置这个flag的话,home页的划屏会有问题
params.width = LayoutParams.WRAP_CONTENT;
params.height = LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.TOP;
mWindowManager.addView(mView, params);
}

private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

/**
* 设置是否可响应点击事件
*
* @param isTouchable
*/
public static void setTouchable(boolean isTouchable) {
if (isTouchable) {
 params.flags = canTouchFlags;
} else {
 params.flags = notTouchFlags;
}
mWindowManager.updateViewLayout(mView, params);

}

/**
* 隐藏弹出框
*/
public static void hidePopupWindow() {
if (isShown && null != mView) {
 mWindowManager.removeView(mView);
 isShown = false;
}
}

public static void setShowTxt(String txt) {
try {
 TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);
 showTv.setText(txt);
 mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
 Log.d(TAG, "setShowTxt: 更新悬浮框错误");
 e.printStackTrace();
 if(e.getMessage().contains("not attached to window manager")){
 mWindowManager.addView(mView, params);
 }
}
}

public static void setShowImg(Bitmap bitmap) {
try {
 ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);
 showImg.setImageBitmap(bitmap);
 mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
 Log.d(TAG, "setShowTxt: 更新悬浮框错误");
 e.printStackTrace();
 if(e.getMessage().contains("not attached to window manager")){
 mWindowManager.addView(mView, params);
 }
}
}

static RelativeLayout rl_drag_showinpop;

private static View setUpView(final Context context, String showtxt) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow,
 null);

TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop);
showTv.setText(showtxt);

rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop);
rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() {
 private float lastX; //上一次位置的X.Y坐标
 private float lastY;
 private float nowX; //当前移动位置的X.Y坐标
 private float nowY;
 private float tranX; //悬浮窗移动位置的相对值
 private float tranY;

@Override
 public boolean onTouch(View v, MotionEvent event) {
 boolean ret = false;
 switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:

// 获取按下时的X,Y坐标
  lastX = event.getRawX();
  lastY = event.getRawY();
  ret = true;

break;
  case MotionEvent.ACTION_MOVE:
  // 获取移动时的X,Y坐标
  nowX = event.getRawX();
  nowY = event.getRawY();
  // 计算XY坐标偏移量
  tranX = nowX - lastX;
  tranY = nowY - lastY;
  params.x += tranX;
  params.y += tranY;

//更新悬浮窗位置
  mWindowManager.updateViewLayout(mView, params);
  //记录当前坐标作为下一次计算的上一次移动的位置坐标
  lastX = nowX;
  lastY = nowY;

break;
  case MotionEvent.ACTION_UP:
  break;
 }
 return ret;
 }
});

return view;
}
}

来源:https://pumpkin.blog.csdn.net/article/details/71155781

标签:Android,悬浮框
0
投稿

猜你喜欢

  • unity使用socket编程实现聊天室功能

    2023-10-18 05:26:04
  • Java8-Stream流操作List去重问题

    2023-11-23 23:29:49
  • Android实现页面跳转的全过程记录

    2023-08-16 21:06:08
  • Java for-each循环使用难题2例(高级使用方法)

    2021-11-15 09:09:06
  • IDEA使用SequenceDiagram插件绘制时序图的方法

    2023-07-03 11:20:07
  • RxJava之网络请求最常见的三种场景

    2023-04-29 19:49:48
  • Java解码H264格式视频流中的图片

    2023-11-24 23:58:24
  • C# 字符串与unicode互相转换实战案例

    2023-12-23 09:29:54
  • Android通过JNI实现守护进程

    2021-11-24 20:38:02
  • C# DateTime.ToString根据不同语言生成相应的时间格式

    2022-04-16 12:34:51
  • android AudioRecorder简单心得分享

    2021-12-13 08:23:46
  • 解决从Map、JSONObject取不存在键值对时的异常情况

    2023-05-08 03:18:27
  • Java控制台输入数组并逆序输出的方法实例 <font color=red>原创</font>

    2023-07-13 23:32:26
  • Android如何监听屏幕旋转

    2021-12-15 06:12:50
  • SpringBoot中如何对actuator进行关闭

    2022-11-30 01:56:37
  • Eclipse+Java+Swing+Mysql实现工资管理系统

    2023-07-26 08:51:05
  • SpringBoot @NotBlank错误的解决方案

    2023-01-12 20:05:59
  • Go Java算法之累加数示例详解

    2023-03-16 00:43:31
  • Flutter应用集成极光推送的实现示例

    2023-06-24 03:51:04
  • SpringMVC之AbstractAnnotationConfigDispatcherSer解读

    2023-06-21 21:42:32
  • asp之家 软件编程 m.aspxhome.com