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;
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2021-01-25

Android实现全局悬浮框

本文实例为大家分享了Android实现全局悬浮框的具体代码,供大家参考,具体内容如下 效果图: 代码实现: Androidmanifest.xml添加弹框权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 自定义悬浮窗类FloatWindow.java public class FloatWindow implements View.OnTouchListener { pr

Android实现通话最小化悬浮框效果

大家在使用主流的视频软件以及直播软件的时候,经常会看到打开视频最小化以后,不是直接关闭,而是在屏幕右下角一个小窗口的样子,本次小编就给大家带来的是用Android实现在视频或者语音通话的时候,最小化也是出现一个悬浮框的效果. 关于音视频通话过程中最小化成悬浮框这个功能的实现,网络上类似的文章很多,但是好像还没看到解释的较为清晰的,这里因为项目需要实现了这样的一个功能,今天我把它记录下来,一方面为了以后用到便于自己查阅,一方面也给有需要的人提供一个思路,让大家少走弯路.这里我也是参考了些有关And

C#实现主窗体最小化后出现悬浮框及双击悬浮框恢复原窗体的方法

本文实例讲述了C#实现主窗体最小化后出现悬浮框及双击悬浮框恢复原窗体的方法.分享给大家供大家参考.具体如下: 这里演示C#实现主窗体最小化后出现悬浮框,双击悬浮框恢复原窗体的效果.类似于360桌面. 主窗体:frmMain using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; u

Android基于腾讯云实时音视频仿微信视频通话最小化悬浮

最近项目中有需要语音.视频通话需求,看到这个像环信.融云等SDK都有具体Demo实现,但咋的领导对腾讯情有独钟啊,IM要用腾讯云IM,不妙的是腾讯云IM并不包含有音视频通话都要自己实现,没办法深入了解腾讯云产品后,决定自己基于腾讯云实时音视频做去语音.视频通话功能.在这里把实现过程记录下为以后用到便于查阅,另一方面也给有需要的人提供一个思路,让大家少走弯路,有可能我的实现的方法不是最好,但是这或许是一个可行的方案,大家不喜勿喷.基于腾讯云实时音视频SDK 6.5.7272版本,腾讯DEMO下载地

JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)

本文实例讲述了JS实现漂亮的窗口拖拽效果.分享给大家供大家参考.具体如下: 这是一款漂亮的JS窗口拖拽效果(改变大小/最小化/最大化/还原/关闭) 特点: ① 窗口可以拖动: ② 窗口可以通过八个方向改变大小: ③ 窗口可以最小化.最大化.还原.关闭: ④ 限制窗口最小宽度/高度. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-win-drug-cha-close-demo/ 具体代码如下: <!DOCTYPE html PUBLIC &qu

Android仿微信视屏悬浮窗效果

在项目中需要对接入的腾讯云音视频,可以悬浮窗显示,悬浮窗可拖拽,并且在悬浮窗不影响其他的activity的焦点. 这个大神的文章Android基于腾讯云实时音视频仿微信视频通话最小化悬浮,他讲的是视频通话时,将远端视频以悬浮窗形式展示,根据他的代码我进行了部分简化 1.悬浮窗效果:点击缩小按钮,将当前远端视屏加载进悬浮窗,且悬浮窗可拖拽,不影响其他界面焦点:点击悬浮窗可返回原来的Activity 2.实现悬浮窗需要: 在androidManifest中申请悬浮窗权限<uses-permissio

如何让批处理程序启动的时候最小化

批处理隐藏运行 隐藏批处理本身 复制代码 代码如下: @echo off if "%1"=="h" goto begin start mshta vbscript:createobject("wscript.shell").run("""%~nx0"" h",0)(window.close)&&exit :begin ::以下为正常批处理命令,不可含有pause set/

js实现右下角可关闭最小化div(可用于展示推荐内容)

本实例使用Javascript实现右下角可关闭最小化div,可以用于展示推荐内容,效果预览网址:http://keleyi.com/keleyi/phtml/xuanfudiv/3.htm 效果图片:  完整源代码: 复制代码 代码如下: <!DOCTYPE html PUBliC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&q

使用jQuery UI的tooltip函数修饰title属性的气泡悬浮框

使用jQuery UI的tooltip()函数,可以使悬浮提示框不再那么千篇一律.点击这里先看看效果吧:http://www.keleyi.com/keleyi/phtml/tooltip/ 以下是完整代码:保存到html文件打开也可以看效果. 复制代码 代码如下: <!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; cha

js和css写一个可以自动隐藏的悬浮框

今天写一个小实例,用js和css写一个可以自动隐藏的悬浮框.css肯定是用来控制样式的,js用来控制器显示与隐藏的.显示与隐藏通常有两种方法实现:1,用js控制其显示属性:2,用js控制其大小. 今天要说的就是通过控制其大小来实现元素的显隐,原理:为其注册鼠标移入.移出的事件,当鼠标移出对象范围,将其宽度设为1,当鼠标再次移入该对象,将其宽度还原.很简单,我们一起看看吧! 隐藏状态: 左边那一条窄线就是隐藏以后的悬浮框. 显示状态: 当鼠标滑到左边的悬浮框上,悬浮框就又显示出来了. CSS样式:

Android使用ImageView实现支持手势缩放效果

TouchImageView继承自ImageView具有ImageView的所有功能:除此之外,还有缩放.拖拽.双击放大等功能,支持viewpager和scaletype,并伴有动画效果. sharedConstructing private void sharedConstructing(Context context) { super.setClickable(true); this.context = context; mScaleDetector = new ScaleGestureDe