1,原文在这里http://blog.csdn.net/qq_17250009/article/details/52908791,我只是把里面的关键步骤给注释了一下,首先来看一下我们的效果,如图(电脑太卡,截图有问题,见谅):

2,来看一下我们的实现步骤,首先创建一个service,用于将我们的布局悬浮到我们的Android系统上去,然后通过activity来启动这个service,所以代码如下:

MainActivity.java

package com.qianmo.suspendcircle;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button; import static android.icu.lang.UCharacter.GraphemeClusterBreak.V;
import static android.os.Build.VERSION_CODES.M; public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_start;
private Button btn_end; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
btn_start = (Button) findViewById(R.id.btn_start);
btn_end = (Button) findViewById(R.id.btn_end);
btn_start.setOnClickListener(this);
btn_end.setOnClickListener(this);
setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); //noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
} return super.onOptionsItemSelected(item);
} @Override
public void onClick(View v) {
switch (v.getId()) {
//启动服务
case R.id.btn_start:
Intent intent = new Intent(MainActivity.this, WindowService.class);
startService(intent);
break;
//停止服务
case R.id.btn_end:
Intent intent1 = new Intent(MainActivity.this, WindowService.class);
stopService(intent1);
break;
}
}
}

MainActivity这个没什么好说的,就是启动service和关闭service而已,主要是那个Service,代码如下:

WindowService.java

package com.qianmo.suspendcircle;

import android.app.ActivityManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView; import java.util.List; /**
* Created by Administrator on 2017/2/9 0009.
* E-Mail:543441727@qq.com
*/ public class WindowService extends Service {
private final String TAG = this.getClass().getSimpleName(); private WindowManager.LayoutParams wmParams;
private WindowManager mWindowManager;
private View mWindowView;
private TextView mPercenrTv; private int mStartX;
private int mStartY;
private int mEndX;
private int mEndY; @Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Create");
initWindowParams();
initView();
addWindowViewZWindow();
initClick();
} /**
* 点击事件和拖拽事件
*/
private void initClick() {
mPercenrTv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
//按下鼠标的时候记录下屏幕的位置
case MotionEvent.ACTION_DOWN:
mStartX = (int) event.getRawX();
mStartY = (int) event.getRawY(); break;
case MotionEvent.ACTION_MOVE:
mEndX = (int) event.getRawX();
mEndY = (int) event.getRawY();
if (needIntercept()) {
//getRawX是触摸位置相对于整个屏幕的位置,getX是控触摸点相对于控件最左边的位置
wmParams.x = (int) event.getRawX() - mWindowView.getMeasuredWidth() / 2;
wmParams.y = (int) event.getRawY() - mWindowView.getMeasuredHeight() / 2;
mWindowManager.updateViewLayout(mWindowView, wmParams);
return true;
}
break;
case MotionEvent.ACTION_UP:
if (needIntercept()) {
return true;
}
break;
} return false;
}
}); mPercenrTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "点击了");
if (isAppAtBackground(WindowService.this)) {
Intent intent = new Intent(WindowService.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}); } /**
* 判断当前应用是前台还是后台
*/
private boolean isAppAtBackground(final Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
return false;
} /**
* 判断是否拦截,根据滑动的距离
*
* @return
*/
private boolean needIntercept() {
if (Math.abs(mStartX - mEndX) > 30 || Math.abs(mStartY - mEndY) > 30) {
return true;
}
return false;
} /**
* 添加View到桌面Window界面上
*/
private void addWindowViewZWindow() {
mWindowManager.addView(mWindowView, wmParams);
} /**
* 初始化加速球控件
*/
private void initView() {
mWindowView = LayoutInflater.from(getApplication()).inflate(R.layout.layout_window, null);
mPercenrTv = (TextView) mWindowView.findViewById(R.id.percentTv);
} /**
* 初始化Window对象的参数
*/
private void initWindowParams() {
//1,获取系统级别的WindowManager
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
wmParams = new WindowManager.LayoutParams(); //2,添加系统参数,确保悬浮框能显示到手机上 //电话窗口。它用于电话交互(特别是呼入)。它置于所有应用程序之上,状态栏之下。
wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
//期望的位图格式。默认为不透明
wmParams.format = PixelFormat.TRANSLUCENT;
//不许获得焦点
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//窗口停靠位置
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; } @Override
public void onDestroy() {
super.onDestroy();
if (mWindowView != null) {
//移除悬浮窗口
Log.i(TAG, "removeView");
mWindowManager.removeView(mWindowView);
}
Log.i(TAG, "onDestroy");
} @Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

 由于注释很详细了,就不给大家说代码了

 最后附上源码地址: https://github.com/543441727/SuspendWindow.git

Android -- 使用WindowManager实现悬浮框效果的更多相关文章

  1. C# 鼠标悬停在datagridview的某单元格,显示悬浮框效果

    今天在做项目时,看到一软件做的悬浮框效果不错,从网上搜罗了一些资料,未见到有十分好的解决办法,只能自已动手,利用datagridview 的ToolTipText 来达到此效果. 以下是我简单实现的代 ...

  2. android自定义listview实现header悬浮框效果

    之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图: 上图中标红的1,2,3,4四张图中,当向上滑动时 ...

  3. android适配全机型悬浮框、视频APP项目、手势操作、Kotlin妹子App、相机图片处理等源码

    Android精选源码 图片滤镜处理,相机滤镜实时处理,相机拍照录制 android仿爱壁纸App更换壁纸效果源码 基于Kotlin+MVP+Retrofit+RxJava+Glide 等架构实现短视 ...

  4. Android悬浮框,在Service中打开悬浮窗;在Service中打开Dialog;

    文章介绍了如何在Service中显示悬浮框,在Service中弹出Dialog,在Service中做耗时的轮询操作: 背景需求: 公司的项目现在的逻辑是这样的:发送一个指令,然后3秒一次轮询去查询这个 ...

  5. uni-app悬浮框模板

    1. uni-app悬浮框模板 1.1. 目标 模仿饿了吗app的悬浮框效果,即上移过程中,中间的某个组件框到顶部后不再上移,呈类似置顶的效果 1.2. 问题 中间遇到fixed固定组件导致flex失 ...

  6. Android音视频通话过程中最小化成悬浮框的实现(类似Android8.0画中画效果)

    关于音视频通话过程中最小化成悬浮框这个功能的实现,网络上类似的文章很多,但是好像还没看到解释的较为清晰的,这里因为项目需要实现了这样的一个功能,今天我把它记录下来,一方面为了以后用到便于自己查阅,一方 ...

  7. Android 应用开机自启和无需权限开启悬浮框

    开机自启主要自定义广播接收类,且需要在清单文件中注册,不要在代码中动态注册. <uses-permission android:name="android.permission.REC ...

  8. 026 Android 带不同类型条目的listview(纯文本类型的条目,图片+文字类型的条目)+读取内存空间、手机进程信息+常驻悬浮框

    1.目标效果 带不同类型条目的listview(纯文本类型的条目,图片+文字类型的条目)+常驻悬浮框 2.页面布局文件 (1)activity_process_manager.xml <?xml ...

  9. android 界面悬浮框实现

    // 定义浮动窗口布局 private View mFloatLayout; // 定义浮动窗口布局对象 private WindowManager.LayoutParams wmParams; // ...

随机推荐

  1. winform 科学计数法转为小数

    先强制转换为decimal. 例如: double xyTolerance = 0.000000008983001; txtXYTolerance.Text = ((decimal)xyToleran ...

  2. [knowledge][linux][sysfs] sysfs文件系统

    https://en.wikipedia.org/wiki/Sysfs http://man7.org/linux/man-pages/man5/sysfs.5.html https://www.ke ...

  3. [qemu][kvm] 在一个vmware虚拟机里安装qemu-kvm虚拟机

    说起来这个需求,简直是傻傻的.但却实实在在的摆在我的面前.... VM无外乎就是为了模拟场景:我现在要的场景就是一台很多个core的linux主机.但是我只有一个装了windows的笔记本.上边有一个 ...

  4. ios 数组打乱顺序

    -(NSMutableArray*)getRandomArrFrome:(NSArray*)arr { NSMutableArray *newArr = [NSMutableArray new]; w ...

  5. es6学习笔记入门总结

    1.let const block 作用域 let 代替var 来声明块级作用域,没有变量提升,只在块内有作用 const 可以声明一个常量,类似于指针,指向某一个引用,这个常量并非一成不变的,但是不 ...

  6. Celery 出现Process 'Worker-5' pid:5608 exited with 'exitcode 1' 问题

    起初我以为是进程PID文件问题,从新删除问题并未解决. 现已解决办法公布如下: pip install --upgrade billiard 原因:依赖的billiard库版本有点低,更新即可

  7. BZOJ3613 南园满地堆轻絮 二分/贪心

    正解:贪心 解题报告: 传送门! 这题似乎是可以二分水过的,,,但数据可以加强一下就能简单把二分卡住了,或者修改下空间限制什么的反正就很容易能卡住 所以这里介绍一个优秀的贪心做法,O(n)的时间复杂度 ...

  8. Git 常用命令和统计代码量

    摘要 分享Git日常操作中常用的命令,分享如何统计在项目中贡献的代码量. 下面列出Git bash常用命令. 1. git clone **(项目地址) 克隆一个git项目到本地,将git项目拉取到本 ...

  9. word2vec参数理解

    之前写了对word2vec的一些简单理解,实践过程中需要对其参数有较深的了解: class gensim.models.word2vec.Word2Vec(sentences=None,size=10 ...

  10. Jenkins打包安卓时提示没同意constraintLayout的license的解决方法

    使用Jenkins打包安卓项目时,报错并失败,错误信息: You have not accepted the license agreements of the following SDK compo ...