此处将为大家介绍关于Android利用WindowManager生成悬浮按钮及悬浮菜单的详细内容,并且为您解答有关androidwindowmanager悬浮的相关问题,此外,我们还将为您介绍关于An
此处将为大家介绍关于Android利用WindowManager生成悬浮按钮及悬浮菜单的详细内容,并且为您解答有关android windowmanager悬浮的相关问题,此外,我们还将为您介绍关于Android Activity源码分析--windowmanager屏幕旋转研究、Android EditText 自动拼写检查报错:android.view.WindowManag、Android WindowManager 全局悬浮窗 + AccessibilityService+ RecyclerView 拖动和点击、Android WindowManager 悬浮窗:不需要申请权限实现悬浮的有用信息。
本文目录一览:- Android利用WindowManager生成悬浮按钮及悬浮菜单(android windowmanager悬浮)
- Android Activity源码分析--windowmanager屏幕旋转研究
- Android EditText 自动拼写检查报错:android.view.WindowManag
- Android WindowManager 全局悬浮窗 + AccessibilityService+ RecyclerView 拖动和点击
- Android WindowManager 悬浮窗:不需要申请权限实现悬浮
Android利用WindowManager生成悬浮按钮及悬浮菜单(android windowmanager悬浮)
简介
本文模仿实现的是360手机卫士基础效果,同时后续会补充一些WindowManager的原理知识。
整体思路
360手机卫士的内存球其实就是一个没有画面的应用程序,整个应用程序的主体是一个Service。我们的程序开始以后,启动一个service,同时关闭activity即可:
public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); startService(new Intent(this,FloatwindowService.class)); finish(); } } import android.os.IBinder; import android.util.Log; import java.util.Timer; import java.util.TimerTask; public class FloatwindowService extends Service { private static final String TAG = FloatwindowService.class.getSimpleName(); public FloatwindowService() { } @Override public int onStartCommand(Intent intent,int flags,int startId) { Log.d(TAG,"on start command"); FloatwindowManager.instance(getApplicationContext()).createFloatwindow(); return super.onStartCommand(intent,flags,startId); } @Override public IBinder onBind(Intent intent) { // Todo: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } }
我们要注意的是,传统的Service默认是运行在UI线程中的,这点与封装了一个Thread和Handler的intentService不同,所以我们可以直接在Service中更改UI相关的内容。
再来看一下FloatwindowManager中的方法:
public void createFloatwindow() { if (isWindowShowing()) return; WindowManager windowManager = getwindowmanger(context); int screenWidth = windowManager.getDefaultdisplay().getWidth(); int screenHeight = windowManager.getDefaultdisplay().getHeight(); if (floatLayout == null) { floatLayout = new FloatLayout(context); if (smallLayoutParams == null) { smallLayoutParams = new WindowManager.LayoutParams(); smallLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; smallLayoutParams.format = PixelFormat.RGBA_8888; smallLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; smallLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; smallLayoutParams.width = FloatLayout.viewWidth; smallLayoutParams.height = FloatLayout.viewHeight; smallLayoutParams.x = screenWidth; smallLayoutParams.y = screenHeight / 2; } } windowManager.addView(floatLayout,smallLayoutParams); }
以及自定义的View:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/small_layout" android:background="@drawable/bg_small" android:orientation="vertical" android:layout_width="60dip" android:layout_height="25dip"> <TextView android:layout_width="match_parent" android:gravity="center" android:text="悬浮窗" android:layout_height="match_parent" /> </LinearLayout>
public class FloatLayout extends LinearLayout { public static int viewWidth; public static int viewHeight; private WindowManager windowManager; public FloatLayout(final Context context) { super(context); windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(R.layout.small_layout,this); View view = findViewById(R.id.small_layout); viewWidth = view.getLayoutParams().width; viewHeight = view.getLayoutParams().height; setonTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v,MotionEvent event) { FloatwindowManager.instance(context).createFloatMenu(); return true; } }); } }
自定义的View除了加载了一个布局,就是设置了一个Touch监听器,用于点击悬浮窗弹出菜单。注意这里要使用 view.getLayoutParams() 来获取视图的宽和高,因为在构造方法中,这个View并没有被measure完成,所以采用view.getHeight得到的宽高是0。
创建菜单的方法类似,同样通过WindowManager:
public void createFloatMenu() { if (menuLayout != null) return; Log.d(TAG,"create float menu"); WindowManager windowManager = getwindowmanger(context); if (menuLayout == null){ menuLayout = new MenuLayout(context); menuLayoutParams = new WindowManager.LayoutParams(); menuLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; menuLayoutParams.format = PixelFormat.RGBA_8888; } windowManager.addView(menuLayout,menuLayoutParams); }
自定义的菜单将背景设置成半透明,同时分成上下两部分,上部分点击删除菜单,下部分是一些展示的内容:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:background="#96000000" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:id="@+id/trans_part" android:orientation="horizontal" android:layout_weight="1" android:layout_height="0dp"></LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_weight="1" android:background="@color/colorPrimary" android:layout_height="0dp"> <TextView android:layout_width="match_parent" android:text="存放content" android:layout_height="match_parent" /> </LinearLayout> </LinearLayout>
public class MenuLayout extends LinearLayout { public MenuLayout(final Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.transparent_layout,this); View view = findViewById(R.id.trans_part); view.setonClickListener(new OnClickListener() { @Override public void onClick(View v) { FloatwindowManager.instance(context).removeMenuLayout(); } }); } }
可以看见,实现悬浮窗,其实就是通过windowManager.addView 时,在LayoutParam 的type设置为TYPE_PHONE,这样你的视图就是系统级视图,可以覆盖在全部程序的最上面。其余的,更多的是自定义view的知识。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
Android Activity源码分析--windowmanager屏幕旋转研究
注意:鄙人看的是6.0的代码
Activity里面还是调用了WindowManager来显示界面。在activity的738行,有这几行代码
private Window mWindow;
private WindowManager mWindowManager;
/*package*/ View mDecor = null; //这就是activity的主view,我也不知道怎么表达会比较好 = =
平常用的setContentView方法,最终是调用了mWindow来处理的。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
mWindow其实一个 PhoneWindow对象,在6169行的attach方法可是看到它的初始化
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);//我在这里
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}
其中mWindowManager是通过mWindow来实现初始化
mWindowManager = mWindow.getWindowManager();
接着看看activity是怎么调用mWindowManager来显示界面,在源码第4772行,可以看到一个setVisible方法,这就是activity展示界面的入口吧
public void setVisible(boolean visible) {
if (mVisibleFromClient != visible) {
mVisibleFromClient = visible;
if (mVisibleFromServer) {
if (visible) makeVisible();//我将要展示界面
else mDecor.setVisibility(View.INVISIBLE);
}
}
}
接着看看makeVisible方法是怎么调用mWindowManager来显示界面。没错,其实就是用addview来显示界面。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
为什么activity能通过android:configChanges="orientation|keyboardHidden" ,能保证界面不重建呢,我们可以看到源码还有一个onWindowAttributesChanged方法。
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
// Update window manager if: we have a view, that view is
// attached to its parent (which will be a RootView), and
// this activity is not embedded.
if (mParent == null) {
View decor = mDecor;
if (decor != null && decor.getParent() != null) {
getWindowManager().updateViewLayout(decor, params);
}
}
}
我估计是用了updateViewLayout来刷新页面,单纯看到这里,activity是怎么监听屏幕旋转呢?
Android EditText 自动拼写检查报错:android.view.WindowManag
需要给EditText设置inputtype属性,将自动拼写检查屏蔽掉.
android:inputType="textNoSuggestions"
Android WindowManager 全局悬浮窗 + AccessibilityService+ RecyclerView 拖动和点击
在做辅助器的时候 要搞一个全局弹窗,用的 RecyclerView 加载了一些按钮,其中遇到一个小问题 ,就是拖动 RecyclerView 时发现手指坐标不对,研究了一下 发现是初始化的问题。
具体看代码中的注释即可。
重点在于
myFloatViewParama.setX( (int) event.getRawX() );
myFloatViewParama.setY((int) event.getRawY());
/**viewGroup 拦截手势 实现 可以拖动viewgroup 时不影响 其中的view的点击事件。
*/
public class MyRecyclerView extends RecyclerView{
private int minTouchSlop=0;
private float mDownX;
private float mDownY;
public MyRecyclerView(@NonNull Context context) {
this(context ,null);
}
public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context ,attrs ,0);
}
public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
private void initView(Context context){
minTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
/**
* todo onInterceptTouchEvent 用来决定是否拦截事件。
* 判断出为拖动手势就把事件拦截下来,实现viewGroup的拖动效果。
* 如果是点击效果 就不拦截事件,让子 view 响应点击事件。
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean interceptd =false ;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
interceptd = false;
mDownX = event.getX();
mDownY = event.getY();
// todo 重要点,要在此处初始化位置参数,否则拖动时效果有偏差。
// 因为 onTouchEvent 的 MotionEvent.ACTION_DOWN 不一定会被触发。
myFloatViewParama.setX( (int) event.getRawX() );
myFloatViewParama.setY((int) event.getRawY());
break;
case MotionEvent.ACTION_MOVE:
float dx = event.getX() - mDownX;
float dy = event.getY() - mDownY;
// 根据滑动的距离来判断是否是拖动操作
interceptd = Math.abs(dx) > minTouchSlop || Math.abs(dy) > minTouchSlop ;
break;
case MotionEvent.ACTION_UP:
interceptd = false;
break;
}
return interceptd;
}
private MyFloatViewParama myFloatViewParama ;
private WindowManager windowManager ;
public void updateConfig(MyFloatViewParama myFloatViewParama, WindowManager windowManager){
this.myFloatViewParama =myFloatViewParama ;
this.windowManager=windowManager;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(myFloatViewParama==null){
return false ;
}
if(windowManager ==null){
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//todo 因为 ViewGroup 中有 子View 被挡触摸时, ViewGroup ACTION_DOWN 没法被
// 触发,所以为了确保触发,要在 onInterceptTouchEvent ACTION_DOWN 中进行初始化。
myFloatViewParama.setX( (int) event.getRawX() );
myFloatViewParama.setY((int) event.getRawY());
break;
case MotionEvent.ACTION_MOVE:
int nowX = (int) event.getRawX();
int nowY = (int) event.getRawY();
int movedX = nowX - myFloatViewParama.getX();
int movedY = nowY - myFloatViewParama.getY();
myFloatViewParama.setX( nowX );
myFloatViewParama.setY( nowY );
myFloatViewParama.getLayoutParams().x = myFloatViewParama.getLayoutParams().x + movedX;
myFloatViewParama.getLayoutParams().y = myFloatViewParama.getLayoutParams().y + movedY;
windowManager.updateViewLayout(this, myFloatViewParama.getLayoutParams());
break;
case MotionEvent.ACTION_UP:
performClick();
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean performClick() {
return super.performClick();
}
}
Android WindowManager 悬浮窗:不需要申请权限实现悬浮
Android WindowManager 悬浮窗:不需要申请权限实现悬浮
附录文章 1 介绍了 Android 平台上的悬浮窗 WindowManager,WindowManager 悬浮窗可以悬浮在 Android 设备上的桌面窗口之上,但是 WindowManager 的使用,必须先申请权限,在一些定制的 Android 操作系统中,有可能会将 WindowManager 悬浮窗的权限一律屏蔽掉,这就导致基于 WindowManager 的 APP 功能难以实现。
然而,可以变通的通过设置 WindowManager 的类型,通过设置 WindowManager 的类型为 WindowManager.LayoutParams.TYPE_TOAST,从而不需要申请 WindowManager 悬浮窗权限就可以使用 WindowManager。
写一个例子。一个简单的不需要任何布局的 main activity:
package zhangphil.demo;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
TextView textView = new TextView(this);
textView.setGravity(Gravity.CENTER);
textView.setBackgroundColor(Color.BLACK);
textView.setText("zhang phil @ csdn");
textView.setTextSize(10);
textView.setTextColor(Color.RED);
//类型是TYPE_TOAST,像一个普通的Android Toast一样。这样就不需要申请悬浮窗权限了。
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST);
//初始化后不首先获得窗口焦点。不妨碍设备上其他部件的点击、触摸事件。
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = 300;
//params.gravity=Gravity.BOTTOM;
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplication(), "不需要权限的悬浮窗实现", Toast.LENGTH_LONG).show();
}
});
WindowManager windowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
windowManager.addView(textView, params);
}
}
代码运行结果:
通过这样的策略实现 WindowManager,可以做很多事情,比如 Android 常见的音乐类 APP 的底部播放条,可以考虑使用这种手段实现。通过 WindowManager 做一个音乐播放条放在 APP 的顶部长期驻留。
附录:
1,《Android WindowManager 悬浮窗》链接:http://blog.csdn.net/zhangphil/article/details/50382966
2,《Android 桌面小部件 AppWidget(1)》链接:http://blog.csdn.net/zhangphil/article/details/50457355
3,《Android 桌面小部件 AppWidget(2)》链接:http://blog.csdn.net/zhangphil/article/details/50461944
4,《Android 桌面小部件 AppWidget:音乐播放器桌面控制部件 Widget(3)》链接:http://blog.csdn.net/zhangphil/article/details/50466844
关于Android利用WindowManager生成悬浮按钮及悬浮菜单和android windowmanager悬浮的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Android Activity源码分析--windowmanager屏幕旋转研究、Android EditText 自动拼写检查报错:android.view.WindowManag、Android WindowManager 全局悬浮窗 + AccessibilityService+ RecyclerView 拖动和点击、Android WindowManager 悬浮窗:不需要申请权限实现悬浮等相关内容,可以在本站寻找。
本文标签: