前言
测试时需要抓取QXDM log,但是需要连接到电脑上,通过adb口下diag命令,打算编写apk,运行时显示一个悬浮球,可以直接通过apk的service去下diag命令。
设置悬浮球是因为录制音频软件时,如果退出当前录音软件窗口,会停止录制。
其实还想过两种方法
1 在录音软件中设置接收音量键事件开始录制,但这个就需要录音软件源码,较难
2 利用下拉菜单,添加图标。这种也可以,但需要修改系统UI,难
相对而言还是apk相对具有普遍性。
需求分析
1 运行软件,显示悬浮球
2 开始及停止录制图标显示
3 应用service中下发diag命令,adb命令
4 关闭应用图标及销毁进程
参考文章
没有写过apk,查看下一些示例。
Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果
Android 悬浮窗、悬浮球开发
Android将Service服务打包jar供三方调用
安卓源码
学习流程
1 环境搭建
2 创建demo,添加按键以及获取窗口参数(长宽)
3 创建服务
3 创建服务
1,2 省略了,已经在如下代码中实现
参考文章:
Android Service教程
一个Android Service小例子
MainActivity.java
package com.example.my_application1;import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.view.WindowManager;
import android.util.DisplayMetrics;
import android.content.Intent;
import android.widget.Toast;public class MainActivity extends AppCompatActivity {private WindowManager mWindowManager;private Button btn_openfloatball;private Button btn_closefloatball;//closefloatballbtnprivate Button btn_closeapp;private TextView text;private TextView text_width;private TextView text_height;private int mWidth,mHeight; //屏幕的宽高private String string_mwidth,string_mheight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// create uiinitUI();}private void start_floatball_service() {Intent intent_start_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);startService(intent_start_floatball_service);}private void stop_floatball_service() {Intent intent_stop_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);stopService(intent_stop_floatball_service);}private void initUI() {// TODO Auto-generated method stubbtn_openfloatball = findViewById(R.id.openfloatballbtn);btn_openfloatball.setOnClickListener(new openfloatballListener());btn_closefloatball = findViewById(R.id.closefloatballbtn);btn_closefloatball.setOnClickListener(new closefloatballListener());btn_closeapp = findViewById(R.id.exitbtn);btn_closeapp.setOnClickListener(new closeappListener());text = (TextView)findViewById(R.id.textView);text.setText("init");//get window manager, get window params: mWidth,mHeightmWindowManager = (WindowManager) this.getWindowManager();DisplayMetrics display = new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(display);mWidth = display.widthPixels;mHeight = display.heightPixels;text_width = (TextView)findViewById(R.id.WidthText);text_width.setText("宽度 "+ mWidth);text_height = (TextView)findViewById(R.id.HeightText);text_height.setText("高度 "+ mHeight);}public class openfloatballListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stub// display status in textview "button on"text = (TextView)findViewById(R.id.textView);text.setText("button on");Toast.makeText(getApplicationContext(), "openfloatball", Toast.LENGTH_LONG).show();// create floatball_servicestart_floatball_service();}}public class closefloatballListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubtext = (TextView)findViewById(R.id.textView);text.setText("button off");// destroy floatball_servicestop_floatball_service();}}public class closeappListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubandroid.os.Process.killProcess(android.os.Process.myPid());}}}
FloatWindowService.java
package com.example.my_application1;import android.app.Service;
import android.widget.Toast;
import android.os.Handler;
import android.os.IBinder;
import android.content.Intent;public class FloatWindowService extends Service {private static final String TAG = "FloatWindowService";@Overridepublic IBinder onBind(Intent arg0) {return null;}@Overridepublic void onCreate() {super.onCreate();
// ALog.e("服务已创建");Toast.makeText(this, "My Service created", Toast.LENGTH_LONG).show();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();Toast.makeText(this, "My Service destroy", Toast.LENGTH_LONG).show();}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintHorizontal_bias="0.47"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.61" /><Buttonandroid:id="@+id/openfloatballbtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="150dp"android:layout_marginTop="162dp"android:layout_marginEnd="167dp"android:text="弹出悬浮窗"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.015" /><Buttonandroid:id="@+id/closefloatballbtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="150dp"android:layout_marginEnd="173dp"android:text="关闭悬浮窗"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.368" /><Buttonandroid:id="@+id/exitbtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="153dp"android:layout_marginEnd="170dp"android:text="关闭app"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="1.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.499" /><TextViewandroid:id="@+id/WidthText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="52dp"android:layout_marginLeft="52dp"android:layout_marginTop="544dp"android:layout_marginEnd="303dp"android:layout_marginRight="303dp"android:layout_marginBottom="168dp"android:text="窗口宽度"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/HeightText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="51dp"android:layout_marginTop="22dp"android:layout_marginEnd="301dp"android:text="窗口高度"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.333"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.82" /></androidx.constraintlayout.widget.ConstraintLayout>
androidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.my_application1"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.My_Application1"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><service android:name=".FloatWindowService"android:enabled="true"><intent-filter><action android:name="com.example.my_application1.FloatWindowService"/></intent-filter></service></application></manifest>
自定义服务类基于Service
public class Service_name extends Service{}
注意class前不能加abstract,刚开始加了抽象修饰符,导致avd运行app时会闪退。
service创建:创建intent,startService最终调用到自定义的onCreate()。
intent连接各个activity和service
注意调用stopService()。
private void start_floatball_service() {Intent intent_start_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);startService(intent_start_floatball_service);
}
用于弹出提示,方便了解当前状态
Toast.makeText(this, “My Service destroy”, Toast.LENGTH_LONG).show();
4 画悬浮球
先了解Activity、Window、View三者关系
注意事项:
1 开显示权限
显示view闪退——未打开上层显示权限
Android: permission denied for window type 2038
2 版本不同,设置的WindowManager.LayoutParams.type不同
悬浮窗权限问题,windowparams.type
3 设置窗口参数
FLAG_NOT_FOCUSABLE和FLAG_NOT_TOUCH_MODAL
FLAG_NOT_TOUCH_MODAL——屏幕上弹窗之外的地方能够点击
布局参数
Android布局基础知识:wrap_content,match_parent,layout_weight
wrap_content:是layout_width和layout_height的属性值之一,表示和自身内容一样的长度。
match_parent:是layout_width和layout_height的属性值之一,表示和父组件一样的长度。
由于设置悬浮窗口
4 显示view函数差异
//不显示图像,原因待查
mWindowManager.addView(ball,viewparams);
原因:参见mainactivity.java中的set_viewparams(),设置的窗口长宽一开始为FloatWindowSmallView.viewWidth,这两个数值未被定义,所以窗口长宽应该是0.所以不显示。
//重新绘制最上层显示,这个要了解下decorview。
//呈现效果是只显示一个红色的球,其他布局全消失
setContentView(ball);
代码
mainactivity.java
package com.example.demo_v2_addservice;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.view.WindowManager;
import android.util.DisplayMetrics;import android.content.Intent;import android.util.Log;import android.widget.Toast;
import android.os.IBinder;
import android.content.Intent;
import android.app.Service;
import android.content.Context;import android.graphics.PixelFormat;
import android.util.DisplayMetrics;
import android.view.Gravity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private static final String TAG_MAIN = "MainActivity";private WindowManager mWindowManager;private WindowManager.LayoutParams viewparams;private Button btn_openfloatball;private Button btn_closefloatball;//closefloatballbtnprivate Button btn_closeapp;private Button btn_openfloatballview_youkai;private TextView text;private TextView text_width;private TextView text_height;private int mWidth,mHeight; //屏幕的宽高private String string_mwidth,string_mheight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);set_viewparams();initUI();}private void set_viewparams() {if (viewparams == null) {viewparams = new WindowManager.LayoutParams();//窗口显示权限viewparams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; //android 8.0 以后使用 //色彩信息viewparams.format = PixelFormat.RGBA_8888;//窗口弹出是其他区域是否可触摸viewparams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// |WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN//窗口长宽------------出错点 //viewparams.width = FloatWindowSmallView.viewWidth;//viewparams.height = FloatWindowSmallView.viewHeight;//窗口长宽,注意与view一致viewparams.width = 100;viewparams.height = 100;//子控件,居左居上viewparams.gravity = Gravity.LEFT | Gravity.TOP;//起始位置 // 相对于创建的窗口位置viewparams.x = 200;viewparams.y = 200;}}private void start_floatball_service() {Intent intent_start_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);startService(intent_start_floatball_service);}private void stop_floatball_service() {Intent intent_stop_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);stopService(intent_stop_floatball_service);}private void initUI() {// TODO Auto-generated method stubbtn_openfloatball = findViewById(R.id.openfloatballbtn);btn_openfloatball.setOnClickListener(new openfloatballListener());btn_closefloatball = findViewById(R.id.closefloatballbtn);btn_closefloatball.setOnClickListener(new closefloatballListener());btn_closeapp = findViewById(R.id.exitbtn);btn_closeapp.setOnClickListener(new closeappListener());btn_openfloatballview_youkai = findViewById(R.id.openfloatballview_youkaibt);btn_openfloatballview_youkai.setOnClickListener(new openfloatballview_youkaiListener());text = (TextView)findViewById(R.id.textView);text.setText("init");//get window managermWindowManager = (WindowManager) this.getWindowManager();// Display display = windowManager.getDefaultDisplay();DisplayMetrics display = new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(display);mWidth = display.widthPixels;mHeight = display.heightPixels;text_width = (TextView)findViewById(R.id.WidthText);text_width.setText("宽度 "+ mWidth);text_height = (TextView)findViewById(R.id.HeightText);text_height.setText("高度 "+ mHeight);}public class openfloatballview_youkaiListener implements OnClickListener {@Overridepublic void onClick(View arg0) {text = (TextView)findViewById(R.id.textView);text.setText("openfloatballview_youkai");
// android.os.Process.killProcess(android.os.Process.myPid());Toast.makeText(getApplicationContext(), "openfloatballview_youkai", Toast.LENGTH_LONG).show();FloatView_youkai ball = new FloatView_youkai(getApplicationContext());if(mWindowManager == null){Toast.makeText(getApplicationContext(), "no wm", Toast.LENGTH_LONG).show();}else{Toast.makeText(getApplicationContext(), "has wm", Toast.LENGTH_LONG).show();Log.d(TAG_MAIN, "draw ball");mWindowManager.addView(ball,viewparams);
// setContentView(ball);}}}}
自定义的悬浮球view
移动还未实现。
注意view的长宽,圆心位置,view起始位置。
FloatView_youkai.java
package com.example.demo_v2_addservice;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;public class FloatView_youkai extends View {private Context mcontext;Paint paint;private float cx;private float cy;private int radius = 50;private int screenWidth;private int screenHeight;public FloatView_youkai(Context context) {super(context);mcontext = context;paint = new Paint();paint.setColor(Color.RED);//设置颜色为红paint.setAntiAlias(true);//设置锯齿状//填充paint.setStyle(Paint.Style.FILL);paint.setStrokeWidth(20);//view 长宽screenWidth = 100;screenHeight = 100;//圆心位置cx = 50;cy = 50;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (cx != 0 && cy != 0) {// paint.setColor(0x9920dde9);canvas.drawCircle(cx, cy, radius, paint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {cx = event.getX();cy = event.getY();Log.i("----------", "TOUCH");return super.onTouchEvent(event);}}
引入一个问题:
显示圆形后,屏幕除了导航栏都无法触摸。
5 悬浮球增加点击移动功能
WindowManager实现浮动在最顶层视图
关于View的setOnTouchListener和setOnClickListener冲突
1 增加点击view事件监测,及刷新view
预计实现效果:弹出红色悬浮球后,若点击悬浮球,则悬浮球变为绿色,再次点击变回红色。
设置view监听(setOnTouchListener)
public class openfloatballview_youkaiListener implements OnClickListener {@Overridepublic void onClick(View arg0) {text = (TextView)findViewById(R.id.textView);text.setText("openfloatballview_youkai");
// android.os.Process.killProcess(android.os.Process.myPid());Toast.makeText(getApplicationContext(), "openfloatballview_youkai", Toast.LENGTH_LONG).show();if(ball == null)ball = new FloatView_youkai(getApplicationContext());if(mWindowManager == null){Toast.makeText(getApplicationContext(), "no wm", Toast.LENGTH_LONG).show();}else{Toast.makeText(getApplicationContext(), "has wm", Toast.LENGTH_LONG).show();Log.d(TAG_MAIN, "draw ball");mWindowManager.addView(ball,viewparams);
// setContentView(ball);}//创建view点击事件监听,监听到点击事件就刷新// mWindowManager.updateViewLayout(ball,viewparams);if (ball != null){Toast.makeText(getApplicationContext(), "create ball listener", Toast.LENGTH_LONG).show();ball.setOnClickListener(new touchfloatballview_youkaiListener());}}}public class touchfloatballview_youkaiListener implements View.OnClickListener{@Overridepublic void onClick(View arg0){if (mWindowManager != null) {Toast.makeText(getApplicationContext(), "change ball color", Toast.LENGTH_LONG).show();ball.paint.setColor(Color.GREEN);//设置颜色为红//需要刷新view,然后通过WindowManager.updateViewLayout更新view显示
// ball.();mWindowManager.updateViewLayout(ball, viewparams);}//(ball,viewparams);}}
注意:添加touch view监听要放在创建球之后。
监听事件做好了,点击悬浮球会提示改变球的颜色。但未实现。
hhh,先移除ball,再添加就可以了.
在view中有touchevent监听,可以在这里修改球的颜色,但是无法记录当前状态。 可以记录当前值,但是私有数据。
尝试将状态变量定义在main_activity中。
public class MainActivity extends AppCompatActivity {private boolean ball_status ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWindowManager = (WindowManager) this.getWindowManager();ball_status = false;//定义view位置及大小参数,不需要view提前创建。set_viewparams();initUI();}public class touchfloatballview_youkaiListener implements View.OnClickListener{@Overridepublic void onClick(View arg0){if (mWindowManager != null) {Toast.makeText(getApplicationContext(), "change ball color", Toast.LENGTH_LONG).show();if(ball_status == false) {ball.paint.setColor(Color.GREEN);//设置颜色为红ball_status = true;}else if(ball_status == true) {ball.paint.setColor(Color.RED);//ball_status = false;}elseLog.d("","error value");//先移除窗口,再添加mWindowManager.removeView(ball);mWindowManager.addView(ball, viewparams);}//(ball,viewparams);}}
}
需要判断是否已移除view,再在activity的destroy函数中移除view
代码已上传。https://github.com/monsterLang/floatingball/tree/master/demo_v2_addservice
package com.example.demo_v2_addservice;import androidx.appcompat.app.AppCompatActivity;import android.graphics.Color;
import android.os.Bundle;import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.view.WindowManager;
import android.util.DisplayMetrics;import android.content.Intent;import android.util.Log;import android.widget.Toast;
import android.os.IBinder;
import android.content.Intent;
import android.app.Service;
import android.content.Context;import android.graphics.PixelFormat;
import android.util.DisplayMetrics;
import android.view.Gravity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private static final String TAG_MAIN = "MainActivity";private WindowManager mWindowManager;private WindowManager.LayoutParams viewparams;private FloatView_youkai ball;private boolean ball_status ;private boolean ball_status_add ;private Button btn_openfloatball;private Button btn_closefloatball;//closefloatballbtnprivate Button btn_closeapp;private Button btn_openfloatballview_youkai;private Button btn_closefloatballview_youkai;private TextView text;private TextView text_width;private TextView text_height;private int mWidth,mHeight; //屏幕的宽高private String string_mwidth,string_mheight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWindowManager = (WindowManager) this.getWindowManager();ball_status = false;ball_status_add = false;//定义view位置及大小参数,不需要view提前创建。set_viewparams();initUI();// //创建view点击事件监听,监听到点击事件就刷新
// // mWindowManager.updateViewLayout(ball,viewparams);
// if (ball != null)
// {
// Toast.makeText(getApplicationContext(), "create ball listener", Toast.LENGTH_SHORT).show();
//
// ball.setOnClickListener(new touchfloatballview_youkaiListener());
// }}private void remove_view() {mWindowManager.removeView(ball);ball_status_add = false;}private void add_view() {mWindowManager.addView(ball, viewparams);ball_status_add = true;}//可以增加命令去抓取logprivate void record_on() {ball.paint.setColor(Color.GREEN);//设置颜色为绿ball_status = true;//qxdm log start record, cmd = diag xxxx}private void record_off() {ball.paint.setColor(Color.RED);//设置颜色为红ball_status = false;//qxdm log stop record and save}private void set_viewparams() {if (viewparams == null) {viewparams = new WindowManager.LayoutParams();//窗口显示权限viewparams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; //android 8.0 以后使用 https://blog.csdn.net/mai763727999/article/details/78983375?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163307717816780264095751%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163307717816780264095751&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-78983375.pc_search_all_es&utm_term=TYPE_APPLICATION_OVERLAY&spm=1018.2226.3001.4187//色彩信息viewparams.format = PixelFormat.RGBA_8888;//窗口弹出是其他区域是否可触摸viewparams.flags =WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;// | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// |WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN// viewparams.width = FloatWindowSmallView.viewWidth;
// viewparams.height = FloatWindowSmallView.viewHeight;//窗口长宽,注意与view一致viewparams.width = 100;viewparams.height = 100;//子控件,居左居上viewparams.gravity = Gravity.LEFT | Gravity.TOP;//起始位置 // 相对于创建的窗口位置viewparams.x = 200;viewparams.y = 200;}}private void start_floatball_service() {Intent intent_start_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);startService(intent_start_floatball_service);}private void stop_floatball_service() {Intent intent_stop_floatball_service = new Intent(MainActivity.this, FloatWindowService.class);stopService(intent_stop_floatball_service);}private void initUI() {// TODO Auto-generated method stubbtn_openfloatball = findViewById(R.id.openfloatballbtn);btn_openfloatball.setOnClickListener(new openfloatballListener());btn_closefloatball = findViewById(R.id.closefloatballbtn);btn_closefloatball.setOnClickListener(new closefloatballListener());btn_closeapp = findViewById(R.id.exitbtn);btn_closeapp.setOnClickListener(new closeappListener());btn_openfloatballview_youkai = findViewById(R.id.openfloatballview_youkaibt);btn_openfloatballview_youkai.setOnClickListener(new openfloatballview_youkaiListener());btn_closefloatballview_youkai = findViewById(R.id.closefloatballview_youkaibt);btn_closefloatballview_youkai.setOnClickListener(new closefloatballview_youkaiListener());text = (TextView)findViewById(R.id.textView);text.setText("init");//get window manager// if (mWindowManager == null)
// {
// text.setText("no window");
// }
// else
// {
// text.setText("have window");
// }// Display display = windowManager.getDefaultDisplay();DisplayMetrics display = new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(display);
// if (display == null)
// {
// text.setText("no display");
// }
// else
// {
// text.setText("have display");
// }mWidth = display.widthPixels;mHeight = display.heightPixels;
// mWidth=mWindowManager.getDefaultDisplay().getWidth();
// mHeight = this.getResources().getDisplayMetrics().heightPixels;//直接自动转int为string,使用valueof失败原因?
// string_mwidth.valueOf(mWidth);
// string_mheight.valueOf(mHeight);text_width = (TextView)findViewById(R.id.WidthText);text_width.setText("宽度 "+ mWidth);text_height = (TextView)findViewById(R.id.HeightText);text_height.setText("高度 "+ mHeight);}public class openfloatballListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stub// display status in textview "button on"text = (TextView)findViewById(R.id.textView);text.setText("button on");Toast.makeText(getApplicationContext(), "openfloatball", Toast.LENGTH_SHORT).show();// create floatball_servicestart_floatball_service();//create floatbutton
// Button floatingButton = new Button(this);
// floatingButton.setText("button");
//
// WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
// WindowManager.LayoutParams.WRAP_CONTENT,
// WindowManager.LayoutParams.WRAP_CONTENT,
// 0, 0,
// PixelFormat.TRANSPARENT
// );//启动悬浮窗口关闭本窗口
// startService(intent);
// finish();}}public class closefloatballListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubtext = (TextView)findViewById(R.id.textView);text.setText("button off");stop_floatball_service();}}public class touchfloatballview_youkaiListener implements View.OnClickListener{@Overridepublic void onClick(View arg0){if (mWindowManager != null) {Toast.makeText(getApplicationContext(), "change ball color", Toast.LENGTH_SHORT).show();
// ball.paint.setColor(Color.GREEN);//设置颜色为红
// ball.cx = 50;
ball.cy = 50;
// ball.();
// mWindowManager.updateViewLayout(ball, viewparams);if(ball_status == false) {record_on();}else if(ball_status == true) {record_off();}elseLog.d("","error value");remove_view();add_view();// if( mWindowManager.IsWindowVisible(ball))
// {
//
// }if(ball.getVisibility() == View.VISIBLE){Toast.makeText(getApplicationContext(), "view display", Toast.LENGTH_SHORT).show();}}//(ball,viewparams);}}public class openfloatballview_youkaiListener implements OnClickListener {@Overridepublic void onClick(View arg0) {text = (TextView)findViewById(R.id.textView);text.setText("openfloatballview_youkai");
// android.os.Process.killProcess(android.os.Process.myPid());Toast.makeText(getApplicationContext(), "openfloatballview_youkai", Toast.LENGTH_SHORT).show();if(ball == null)ball = new FloatView_youkai(getApplicationContext());if(mWindowManager == null){Toast.makeText(getApplicationContext(), "no wm", Toast.LENGTH_SHORT).show();}else{Toast.makeText(getApplicationContext(), "has wm", Toast.LENGTH_SHORT).show();Log.d(TAG_MAIN, "draw ball");if(ball_status_add == false) {add_view();}
// mWindowManager.addView(ball, viewparams);
// setContentView(ball);}//创建view点击事件监听,监听到点击事件就刷新// mWindowManager.updateViewLayout(ball,viewparams);if (ball != null){Toast.makeText(getApplicationContext(), "create ball listener", Toast.LENGTH_SHORT).show();ball.setOnClickListener(new touchfloatballview_youkaiListener());}}}public class closefloatballview_youkaiListener implements OnClickListener {@Overridepublic void onClick(View arg0) {text = (TextView)findViewById(R.id.textView);text.setText("closefloatballview_youkai");
// android.os.Process.killProcess(android.os.Process.myPid());Toast.makeText(getApplicationContext(), "closefloatballview_youkai", Toast.LENGTH_SHORT).show();// ball = new FloatView_youkai(getApplicationContext());if(mWindowManager == null){Toast.makeText(getApplicationContext(), "no wm", Toast.LENGTH_SHORT).show();}else{Toast.makeText(getApplicationContext(), "has wm", Toast.LENGTH_SHORT).show();Log.d(TAG_MAIN, "draw ball");//如果显示了view就移除,按理说不应该用这个,也可能view不显示在最前。
// if(ball.getVisibility() == View.VISIBLE)if(ball_status_add == true){remove_view();if(ball_status == true) {record_off();}}
// setContentView(ball);}// if(ball_status_add == false)
// {
// Log.d(TAG_MAIN, "view no display");
// Toast.makeText(getApplicationContext(), "view no display", Toast.LENGTH_SHORT).show();
// }
// else if(ball_status_add == true)
// {
// Log.d(TAG_MAIN, "view GONE");
// Toast.makeText(getApplicationContext(), "view GONE", Toast.LENGTH_SHORT).show();
// }}}public class closeappListener implements OnClickListener {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stub// if (ball_status_add == true)if(ball.getVisibility() == View.VISIBLE)mWindowManager.removeView(ball);//待添加判断是否显示球,若显示则移除,若不显示,则删除ball变量android.os.Process.killProcess(android.os.Process.myPid());}}}
package com.example.demo_v2_addservice;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;public class FloatView_youkai extends View {private Context mcontext;private FloatView_youkai view;private boolean status_ball;Paint paint;private float cx;private float cy;private int radius = 50;private int screenWidth;private int screenHeight;private void setOnTouchListener() {if (view != null){view.setOnTouchListener(new View.OnTouchListener(){@Overridepublic boolean onTouch(View v, MotionEvent event) {paint.setColor(Color.GREEN);//设置颜色为红Log.d("----------", "TOUCH");return true;}});}}public FloatView_youkai(Context context) {super(context);mcontext = context;paint = new Paint();paint.setColor(Color.RED);//设置颜色为红paint.setAntiAlias(true);//设置锯齿状//填充paint.setStyle(Paint.Style.FILL);paint.setStrokeWidth(20);// //获取屏幕参数
// WindowManager wm = (WindowManager) mcontext.getSystemService(Context.WINDOW_SERVICE);
// DisplayMetrics display = new DisplayMetrics();
// wm.getDefaultDisplay().getMetrics(display);// screenWidth = display.widthPixels;
// screenHeight = display.heightPixels;//view 长宽screenWidth = 100;screenHeight = 100;//圆心位置cx = 50;cy = 50;//view 自身设置监听,好像不太行view = this;
// setOnTouchListener();status_ball = false;}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (cx != 0 && cy != 0) {// paint.setColor(0x9920dde9);canvas.drawCircle(cx, cy, radius, paint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {// cx = event.getX();
// cy = event.getY();
// if(status_ball == false) {
// view.paint.setColor(Color.RED);//设置颜色为红
// status_ball = true;
// }
// else if(status_ball == true) {
// view.paint.setColor(Color.GREEN);//
// status_ball = false;
// }
// else
// Log.d("","error value");Log.i("----------", "TOUCH");return super.onTouchEvent(event);}}
2 增加移动事件监测
需要修改圆心位置
点击和移动事件区分开
待办
添加log
import android.util.Log;public class MainActivity extends AppCompatActivity {private static final String TAG_MAIN = "MainActivity";
}//使用处
Log.d(TAG_MAIN, "draw ball");
报错
Hardcoded string “floatball”, should use @string
resource
Hardcoded string 汉字, should use @string resource警告
参照文章
使用WindowManager添加View——悬浮窗口的基本原理 - cpacm - 博客园
WindowManager addView弹窗功能_feiyangxiaomi的专栏-CSDN博客
WindowManager实现浮动在最顶层视图 - 程序员大本营
WindowManager实现浮动在最顶层视图_搬砖路上的博客-CSDN博客
view 的绘制和刷新_luyuqin0115的博客-CSDN博客_view刷新
android onTouchEvent和setOnTouchListener中onTouch的区别 - 淡泊名利 - 博客园
理解Window和WindowManager(一)window的添加删除更新view_dev晴天的博客-CSDN博客
Android事件分发、View事件Listener全解析_学无常师,负笈不远险阻-CSDN博客
View的setOnClickListener的添加方法_小宇宙的小怪兽-CSDN博客
请教关于 View.OnClickListener() 的问题_rongliu1741的博客-CSDN博客
Android 源码解析之WindowManager更新窗口_小王君的专栏-CSDN博客
Cross Reference: /frameworks/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java