`

使用WindowManager创建可拖动的悬浮按钮

阅读更多
场景描述:
界面上有一个浮动按钮,可以用手指拖动,点击该按钮触发事件,我这里是启动扫描,留了一个空实现。
private WindowManager wm;
	private View view;// 浮动按钮

	/**
	 * 添加悬浮View
	 * @param paddingBottom 悬浮View与屏幕底部的距离
	 */
	protected void createFloatView(int paddingBottom) {
		int w = 200;// 大小
		wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
		view = getLayoutInflater().inflate(R.layout.floatview, null);
		final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
		params.type = LayoutParams.TYPE_BASE_APPLICATION;// 所有程序窗口的“基地”窗口,其他应用程序窗口都显示在它上面。
		params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
				| LayoutParams.FLAG_NOT_FOCUSABLE;
		params.format = PixelFormat.TRANSLUCENT;// 不设置这个弹出框的透明遮罩显示为黑色
		params.width = w;
		params.height = w;
		params.gravity = Gravity.TOP | Gravity.LEFT;
		int screenWidth = getResources().getDisplayMetrics().widthPixels;
		int screenHeight = getResources().getDisplayMetrics().heightPixels;
		params.x = screenWidth - w;
		params.y = screenHeight - w - paddingBottom;
		view.setBackgroundColor(Color.TRANSPARENT);
		view.setVisibility(View.VISIBLE);
		view.setOnTouchListener(new OnTouchListener() {
			// 触屏监听
			float lastX, lastY;
			int oldOffsetX, oldOffsetY;
			int tag = 0;// 悬浮球 所需成员变量

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				final int action = event.getAction();
				float x = event.getX();
				float y = event.getY();
				if (tag == 0) {
					oldOffsetX = params.x; // 偏移量
					oldOffsetY = params.y; // 偏移量
				}
				if (action == MotionEvent.ACTION_DOWN) {
					lastX = x;
					lastY = y;
				} else if (action == MotionEvent.ACTION_MOVE) {
					params.x += (int) (x - lastX) / 3; // 减小偏移量,防止过度抖动
					params.y += (int) (y - lastY) / 3; // 减小偏移量,防止过度抖动
					tag = 1;
					wm.updateViewLayout(view, params);
				} else if (action == MotionEvent.ACTION_UP) {
					int newOffsetX = params.x;
					int newOffsetY = params.y;
					// 只要按钮一动位置不是很大,就认为是点击事件
					if (Math.abs(oldOffsetX - newOffsetX) <= 20
							&& Math.abs(oldOffsetY - newOffsetY) <= 20) {
						onFloatViewClick();
					} else {
						tag = 0;
					}
				}
				return true;
			}
		});
		wm.addView(view, params);
	}

	/**
	 * 点击浮动按钮触发事件,需要override该方法
	 */
	protected void onFloatViewClick() {

	}

	/**
	 * 将悬浮View从WindowManager中移除,需要与createFloatView()成对出现
	 */
	protected void removeFloatView() {
		if (wm != null && view != null) {
			wm.removeViewImmediate(view);
//			wm.removeView(view);//不要调用这个,WindowLeaked
			view = null;
			wm = null;
		}
	}
	/**
	 * 隐藏悬浮View
	 */
	protected void hideFloatView() {
		if (wm != null && view != null&&view.isShown()) {
			view.setVisibility(View.GONE);
		}
	}
	/**
	 * 显示悬浮View
	 */
	protected void showFloatView(){
		if (wm != null && view != null&&!view.isShown()) {
			view.setVisibility(View.VISIBLE);
		}
	}




或者使用Service
只要启动这个 Service,就会创建一个随手指移动的悬浮窗,关闭 Service 会移除悬浮窗。另外由于这里设置了 Window 的 type 为 TYPE_PHONE, 所以需要 SYSTEM_ALERT_WINDOW 权限,也可以使用 TYPE_TOAST。
public class WindowService extends Service {
		WindowManager windowManager;
		ImageView imageView;

		public WindowService() {
		}

		@Override
		public void onCreate() {
			super.onCreate();
			windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
		}

		@Override
		public IBinder onBind(Intent intent) {
			return null;
		}

		@Override
		public int onStartCommand(Intent intent, int flags, int startId) {
			if (imageView == null) {
				installWindow();
			}
			return START_STICKY;
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
			if (imageView != null) {
				windowManager.removeView(imageView);
			}
		}

		private void installWindow() {
			imageView = new ImageView(this.getBaseContext());
			imageView.setImageResource(R.mipmap.ic_launcher_round);
			final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
			layoutParams.format = PixelFormat.TRANSPARENT;
			layoutParams.width = 200;
			layoutParams.height = 200;
			layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
			layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
			layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
					| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
					| WindowManager.LayoutParams.FLAG_FULLSCREEN
					| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
			windowManager.addView(imageView, layoutParams);
			imageView.setOnTouchListener(new View.OnTouchListener() {
				@Override
				public boolean onTouch(View v, MotionEvent event) {
					layoutParams.x = (int) event.getRawX() - 100;
					layoutParams.y = (int) event.getRawY() - 100;
					windowManager.updateViewLayout(imageView, layoutParams);
					return true;
				}

			});
		}
	}




http://blog.csdn.net/manymore13
import com.mobovip.bgr.R;

import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;

/**
 * @author manymore13
 * @Blog <a
 *       href="http://blog.csdn.net/manymore13">http://blog.csdn.net/manymore13
 *       </a>
 * @version 1.0 floatView = new FloatView(this); // 创建窗体
 *          floatView.setOnClickListener(this); //
 *          设置事件,你需要实现FloatView里的onclick接口 floatView.show(); // 显示该窗体
 *          floatView.hide(); // 隐藏窗体
 */
public class FloatView extends ImageView {

	private Context c;
	private float mTouchX;
	private float mTouchY;
	private float x;
	private float y;
	private int startX;
	private int startY;
	private int imgId = R.drawable.ic_launcher;
	private int controlledSpace = 20;
	private int screenWidth;
	private int screenHeight;
	boolean isShow = false;
	private OnClickListener mClickListener;

	private WindowManager windowManager;

	private WindowManager.LayoutParams windowManagerParams = new WindowManager.LayoutParams();

	public FloatView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public FloatView(Context c) {
		super(c);
		initView(c);
	}

	// 初始化窗体
	public void initView(Context c) {
		windowManager = (WindowManager) c.getApplicationContext()
				.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics dm=getResources().getDisplayMetrics();
		screenWidth = dm.widthPixels;
		screenHeight = dm.heightPixels;
		this.setImageResource(imgId);
		windowManagerParams.type = LayoutParams.TYPE_PHONE;
		windowManagerParams.format = PixelFormat.RGBA_8888; // 背景透明
		windowManagerParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
				| LayoutParams.FLAG_NOT_FOCUSABLE;
		// 调整悬浮窗口至左上角,便于调整坐标
		windowManagerParams.gravity = Gravity.LEFT | Gravity.TOP;
		// 以屏幕左上角为原点,设置x、y初始值
		windowManagerParams.x = 0;
		windowManagerParams.y = screenHeight>>1;
		// 设置悬浮窗口长宽数据
		windowManagerParams.width = LayoutParams.WRAP_CONTENT;
		windowManagerParams.height = LayoutParams.WRAP_CONTENT;

	}

	public void setImgResource(int id) {
		imgId = id;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		x = event.getRawX();
		y = event.getRawY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN: {
			mTouchX = event.getX();
			mTouchY = event.getY();
			startX = (int) event.getRawX();
			startY = (int) event.getRawY();
			break;

		}
		case MotionEvent.ACTION_MOVE: {
			updateViewPosition();
			break;
		}
		case MotionEvent.ACTION_UP: {
			
			if (Math.abs(x - startX) < controlledSpace
					&& Math.abs(y - startY) < controlledSpace) {
				if (mClickListener != null) {
					mClickListener.onClick(this);
				}
			}
//			Log.i("tag", "x=" + x + " startX+" + startX + " y=" + y
//					+ " startY=" + startY);
			if (x <= screenWidth / 2) {
				x = 0;
			} else {
				x = screenWidth;
			}

			updateViewPosition();

			break;
		}
		}

		return super.onTouchEvent(event);
	}

	// 隐藏该窗体
	public void hide() {
		if (isShow) {
			windowManager.removeView(this);
			isShow = false;
		}

	}

	// 显示该窗体
	public void show() {
		if (isShow == false) {
			windowManager.addView(this, windowManagerParams);
			isShow = true;
		}

	}

	@Override
	public void setOnClickListener(OnClickListener l) {
		this.mClickListener = l;
	}

	private void updateViewPosition() {
		// 更新浮动窗口位置参数
		windowManagerParams.x = (int) (x - mTouchX);
		windowManagerParams.y = (int) (y - mTouchY);
		windowManager.updateViewLayout(this, windowManagerParams); // 刷新显示
	}
}




一个Android布局可以拖动排序子控件:RearrangeableLayout
https://github.com/rajasharan/RearrangeableLayout
分享到:
评论

相关推荐

    可移动的悬浮按钮(WindowManager)

    在这里,我们将会深入探讨如何使用WindowManager来实现一个可移动的悬浮按钮,并结合`FloatView`的实现进行讨论。 首先,让我们了解`WindowManager`。它是Android的系统服务,可以通过`Context.getSystemService...

    可拖动的悬浮窗+对话框悬浮窗

    在Android开发中,创建一个可拖动的悬浮窗和对话框悬浮窗是一项常见的需求,尤其在实现自定义功能或者增强用户体验时。这个压缩包文件很可能包含了一套完整的示例代码,帮助开发者理解并实现这样的功能。下面将详细...

    安卓悬浮窗相关-悬浮按钮随意拖动通过接口回调处理点击事件.rar

    本压缩包文件“安卓悬浮窗相关-悬浮按钮随意拖动通过接口回调处理点击事件.rar”包含了实现这种功能的相关代码示例,让我们来详细探讨一下其涉及的关键知识点。 首先,悬浮窗在安卓中通常通过`WindowManager`服务和...

    (android实现可拖拽的悬浮框)使用示例

    通过这个 `(android实现可拖拽的悬浮框)使用示例`,开发者可以了解到如何结合`WindowManager`、触摸事件监听以及自定义服务来创建一个可拖动的悬浮窗。这只是一个基础的实现,实际应用中可能需要考虑更多的细节,如...

    android桌面悬浮按钮

    在Android中,创建悬浮窗通常需要使用` WindowManager`服务来添加和管理窗口,并通过`LayoutParams`来设置窗口的属性,如位置、大小和类型等。`FloatsWindowView`可能包含了自定义布局和触摸事件处理,以便实现可...

    android仿Assistive Touch悬浮按钮

    - 在Service中,使用`WindowManager`接口添加一个布局到屏幕顶部,这个布局就是悬浮按钮。 - 为了使按钮始终显示在其他应用之上,需要设置窗口类型为`WindowManager.LayoutParams.TYPE_SYSTEM_ALERT`。 2. **悬浮...

    android 添加窗口悬浮按钮,360悬浮框原理

    3. 创建一个`WindowManager`实例,用来管理悬浮按钮的显示和隐藏: ```java WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager....

    Android悬浮按钮

    通过组合这些功能,你可以在应用中创建一个可自由移动、显示网络图片并带有旋转动画的悬浮按钮。`MyTabTest`可能是项目中的一个测试模块或者一个具体的功能类,具体实现细节需要根据源代码来分析。

    可拖动点击的桌面悬浮窗

    在安卓平台上,实现一个可拖动且点击响应的桌面悬浮窗是一项常见的需求,尤其是在开发辅助工具或者自定义功能的应用时。这个标题“可拖动点击的桌面悬浮窗”所涉及的知识点主要涵盖以下几个方面: 1. **悬浮窗权限*...

    android 应用内部悬浮可拖动按钮简单实现代码

    在获取 WindowManager 对象和屏幕尺寸后,需要创建一个 WindowManager.LayoutParams 对象,用于设置悬浮按钮的布局参数。在该对象中,需要设置 Window 的 type、format、gravity、flags 等属性。其中,type 属性用于...

    悬浮窗的使用

    在标题中提到的"悬浮窗的使用",主要涉及到如何创建和管理这样的窗口。悬浮窗通常用于提供便捷的操作,如系统监控、快捷工具或者特定应用的通知,例如360安全卫士的“加速球”。 在Android平台上,实现悬浮窗主要...

    安卓悬浮框源码,可拖动

    在Android开发中,悬浮窗(Float View)是一种特殊类型的窗口,它可以在所有应用之上显示,让用户在使用其他应用的同时查看或操作悬浮窗内容。这种功能常用于音乐播放器、即时通讯应用以及各种辅助工具中。标题提到...

    Android 实现可任意拖动的悬浮窗功能(类似悬浮球)

    Android 可任意拖动的悬浮窗功能实现 Android 操作系统中,悬浮窗功能是一种常见的用户界面元素,能够提供更多的交互方式。今天,我们将介绍如何实现 ...这种功能可以在很多场景下使用,例如悬浮球、悬浮按钮等。

    Xamarin.Android悬浮框

    首先,悬浮框在Android中通常是通过`WindowManager`服务和自定义布局来创建的。在Xamarin.Android中,你需要先获取到`WindowManager`的实例,这可以通过`Context.GetSystemService`方法并传入`WindowService`来实现...

    自定义的悬浮窗,可托拉拽

    本文将深入探讨如何在Android中创建一个可托拉拽的自定义悬浮窗,并在任何Activity界面上实现其功能,最后讨论如何删除它。 首先,要创建自定义悬浮窗,我们需要了解`WindowManager`接口。`WindowManager`是Android...

    android实现悬浮窗功能,无需权限

    1. 创建一个悬浮窗布局:在XML文件中设计悬浮窗的UI结构,例如包含一个ImageView或TextView,以展示所需内容。 ```xml &lt;!-- res/layout/my_float_view.xml --&gt; android:layout_width="wrap_content" android:...

    android伸缩式悬浮窗

    它允许我们将视图(View)添加到窗口层级结构中,这些视图可以作为浮动窗口显示在应用之上或之下,比如我们常见的悬浮按钮、悬浮窗等。 实现可伸缩式悬浮窗的关键步骤如下: 1. 创建悬浮窗布局: 首先,我们需要...

Global site tag (gtag.js) - Google Analytics