效果如上图所示

安卓小白记录学习过程。一起交流学习。有问题请多多指出。话不多说进入正题。

一、思路

首先明确需求,我们需要做一个右划弹出一个快捷切换应用的弹窗。

悬浮窗可以在其他应用上层显示出来,那么我们可以设置一个透明的悬浮窗让它显示在其他应用上层,然后通过这个悬浮窗只需要写一个滑动的监听事件。满足条件去弹出一个PopupWindow即可。那么有了思路按部就班去实现就OK了。

二、实现

(1)第一步实现一个显示在其他应用上层的悬浮窗

主布局简单写个按钮去弹出悬浮窗就行,这个看情况而定。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><Buttonandroid:id="@+id/button_star_service"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="启动悬浮窗"/></LinearLayout>

我们需要右划去弹出PopupWindow那么我们可以写一个透明的控件放在右边缘,我用的是一个宽10dp和和手机屏幕等高的一个TextView,用什么都可以只要看不到即可。

float_window.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="match_parent"><TextViewandroid:id="@+id/textview_float_window"android:layout_width="10dp"android:layout_height="match_parent"/></LinearLayout>

MainActivity简单写个Button点击事件去启动悬浮窗就行了

MainActivity.java

package com.example.floatingwindow;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.view.View;
import android.widget.Button;public class MainActivity extends AppCompatActivity implements View.OnClickListener {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button = (Button) findViewById(R.id.button_star_service);button.setOnClickListener(this::onClick);}@Overridepublic void onClick(View view) {/*** 启动悬浮窗并且获取悬浮窗权限*/FloatWindowManager floatWindowManager = new FloatWindowManager();floatWindowManager.requestPermission(this);floatWindowManager.initManager(this);floatWindowManager.showFloatWindow();}
}

FloatWindowManager用于创建悬浮窗,和设置悬浮窗的滑动事件

FloatWindowManager.java

package com.example.floatingwindow;import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;public class FloatWindowManager {private volatile static FloatWindowManager mInstance;private WindowManager mWindowManager;private Context mContext;private WindowManager.LayoutParams mLayoutParams;private int layoutY;private int layoutX;private View view;public static synchronized FloatWindowManager getInstance() {if (mInstance == null) {synchronized (FloatWindowManager.class) {if (mInstance == null) {mInstance = new FloatWindowManager();}}}return mInstance;}/*** 创建悬浮窗* @param context* @return*/public FloatWindowManager initManager(Context context) {mContext = context;mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);showWindow();return this;}/*** 是否有悬浮框权限** @return*/public boolean requestPermission(Context context) {return SettingsCompat.canDrawOverlays(context, true, false);}/*** 加载 悬浮窗   没有内容*/private synchronized void showWindow() {view = LayoutInflater.from(mContext).inflate(R.layout.float_window,null);mLayoutParams = new WindowManager.LayoutParams();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;}mLayoutParams.format = PixelFormat.RGBA_8888; //窗口透明mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; //窗口位置mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();layoutY = 0;layoutX = displayMetrics.widthPixels;mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;mLayoutParams.x = layoutX;mLayoutParams.y = layoutY;setListener();}/*** 显示悬浮窗*/public void showFloatWindow(){mWindowManager.addView(view,mLayoutParams);}/*** 设置 悬浮窗 view 滑动事件*/private void setListener() {if (view != null) {view.setOnTouchListener(new View.OnTouchListener() {private int moveX;   //平移距离int startX, startY,endX,endY;  //起始点boolean isMove;  //是否在移动long startTime;//划动时间boolean downMove = false;@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startX = (int) event.getX();startY = (int) event.getY();startTime = System.currentTimeMillis();isMove = false;downMove = false;return false;case MotionEvent.ACTION_MOVE:return true;case MotionEvent.ACTION_UP:long curTime = System.currentTimeMillis();endX = (int) event.getX();endY = (int) event.getY();isMove = curTime - startTime > 100;if (isMove && Math.abs(endX-startX)>Math.abs(endY-startY)) {//判断左右滑动距离大于上下滑动距离并且滑动时间大于100PopupWindow mPopupWindow = new PopupWindow();mPopupWindow.popupWindow(mContext,view);}return isMove;}return false;}});}}
}

SettingsCompat获取悬浮窗权限

SettingsCompat.java

package com.example.floatingwindow;import android.Manifest;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.provider.Settings;
import android.widget.Toast;import java.lang.reflect.Method;
import java.util.List;public class SettingsCompat {private static final int OP_WRITE_SETTINGS = 23;private static final int OP_SYSTEM_ALERT_WINDOW = 24;/*** 检查悬浮窗权限  当没有权限,跳转到权限设置界面** @param context          上下文* @param isShowDialog     没有权限,是否弹框提示跳转到权限设置界面* @param isShowPermission 是否跳转权限开启界面* @return true 有权限   false 没有权限(跳转权限界面、权限失败 提示用户手动设置权限)*/public static boolean canDrawOverlays(Context context, boolean isShowDialog, boolean isShowPermission) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!Settings.canDrawOverlays(context)) {if (isShowDialog) {//去授权SettingsCompat.manageDrawOverlays(context);} else if (isShowPermission) {manageDrawOverlays(context);}return false;}return true;} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {if (checkOp(context, OP_SYSTEM_ALERT_WINDOW)) {return true;} else {if (isShowPermission)startFloatWindowPermissionErrorToast(context);return false;}} else {return true;}}/*** 打开 悬浮窗 授权界面** @param context*/public static void manageDrawOverlays(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {try {Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + context.getPackageName()));intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);} catch (Exception e) {e.printStackTrace();startFloatWindowPermissionErrorToast(context);}} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {if (!manageDrawOverlaysForRom(context)) {startFloatWindowPermissionErrorToast(context);}}}/*** 权限设置 失败提示。** @param context*/public static void startFloatWindowPermissionErrorToast(Context context) {if (context != null)Toast.makeText(context, "进入设置页面失败,请手动开启悬浮窗权限", Toast.LENGTH_SHORT).show();}private static boolean manageDrawOverlaysForRom(Context context) {if (RomUtil.isMiui()) {return manageDrawOverlaysForMiui(context);}if (RomUtil.isEmui()) {return manageDrawOverlaysForEmui(context);}if (RomUtil.isFlyme()) {return manageDrawOverlaysForFlyme(context);}if (RomUtil.isOppo()) {return manageDrawOverlaysForOppo(context);}if (RomUtil.isVivo()) {return manageDrawOverlaysForVivo(context);}if (RomUtil.isQiku()) {return manageDrawOverlaysForQihu(context);}if (RomUtil.isSmartisan()) {return manageDrawOverlaysForSmartisan(context);}return false;}private static boolean checkOp(Context context, int op) {AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);try {Method method = AppOpsManager.class.getDeclaredMethod("checkOp", int.class, int.class, String.class);return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());} catch (Exception e) {}return false;}// 可设置Android 4.3/4.4的授权状态private static boolean setMode(Context context, int op, boolean allowed) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {return false;}AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);try {Method method = AppOpsManager.class.getDeclaredMethod("setMode", int.class, int.class, String.class, int.class);method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName(), allowed ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);return true;} catch (Exception e) {}return false;}/*** 跳转界面** @param context* @param intent* @return*/private static boolean startSafely(Context context, Intent intent) {List<ResolveInfo> resolveInfos = null;try {resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);if (resolveInfos != null && resolveInfos.size() > 0) {intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);return true;}} catch (Exception e) {e.printStackTrace();}return false;}// 小米private static boolean manageDrawOverlaysForMiui(Context context) {Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");intent.putExtra("extra_pkgname", context.getPackageName());intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");if (startSafely(context, intent)) {return true;}intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");if (startSafely(context, intent)) {return true;}// miui v5 的支持的android版本最高 4.x// http://www.romzj.com/list/search?keyword=MIUI%20V5#search_resultif (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {Intent intent1 = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent1.setData(Uri.fromParts("package", context.getPackageName(), null));return startSafely(context, intent1);}return false;}private final static String HUAWEI_PACKAGE = "com.huawei.systemmanager";// 华为private static boolean manageDrawOverlaysForEmui(Context context) {Intent intent = new Intent();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {intent.setClassName(HUAWEI_PACKAGE, "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");if (startSafely(context, intent)) {return true;}}// Huawei Honor P6|4.4.4|3.0intent.setClassName(HUAWEI_PACKAGE, "com.huawei.notificationmanager.ui.NotificationManagmentActivity");intent.putExtra("showTabsNumber", 1);if (startSafely(context, intent)) {return true;}intent.setClassName(HUAWEI_PACKAGE, "com.huawei.permissionmanager.ui.MainActivity");if (startSafely(context, intent)) {return true;}return false;}// VIVOprivate static boolean manageDrawOverlaysForVivo(Context context) {// 不支持直接到达悬浮窗设置页,只能到 i管家 首页Intent intent = new Intent("com.iqoo.secure");intent.setClassName("com.iqoo.secure", "com.iqoo.secure.MainActivity");// com.iqoo.secure.ui.phoneoptimize.SoftwareManagerActivity// com.iqoo.secure.ui.phoneoptimize.FloatWindowManagerreturn startSafely(context, intent);}// OPPOprivate static boolean manageDrawOverlaysForOppo(Context context) {Intent intent = new Intent();intent.putExtra("packageName", context.getPackageName());// OPPO A53|5.1.1|2.1intent.setAction("com.oppo.safe");intent.setClassName("com.oppo.safe", "com.oppo.safe.permission.floatwindow.FloatWindowListActivity");if (startSafely(context, intent)) {return true;}// OPPO R7s|4.4.4|2.1intent.setAction("com.color.safecenter");intent.setClassName("com.color.safecenter", "com.color.safecenter.permission.floatwindow.FloatWindowListActivity");if (startSafely(context, intent)) {return true;}intent.setAction("com.coloros.safecenter");intent.setClassName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity");return startSafely(context, intent);}// 魅族private static boolean manageDrawOverlaysForFlyme(Context context) {Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity");intent.putExtra("packageName", context.getPackageName());return startSafely(context, intent);}// 360private static boolean manageDrawOverlaysForQihu(Context context) {Intent intent = new Intent();intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity");if (startSafely(context, intent)) {return true;}intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");return startSafely(context, intent);}// 锤子private static boolean manageDrawOverlaysForSmartisan(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {return false;}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// 锤子 坚果|5.1.1|2.5.3Intent intent = new Intent("com.smartisanos.security.action.SWITCHED_PERMISSIONS_NEW");intent.setClassName("com.smartisanos.security", "com.smartisanos.security.SwitchedPermissions");intent.putExtra("index", 17); // 不同版本会不一样return startSafely(context, intent);} else {// 锤子 坚果|4.4.4|2.1.2Intent intent = new Intent("com.smartisanos.security.action.SWITCHED_PERMISSIONS");intent.setClassName("com.smartisanos.security", "com.smartisanos.security.SwitchedPermissions");intent.putExtra("permission", new String[]{Manifest.permission.SYSTEM_ALERT_WINDOW});//        Intent intent = new Intent("com.smartisanos.security.action.MAIN");//        intent.setClassName("com.smartisanos.security", "com.smartisanos.security.MainActivity");return startSafely(context, intent);}}
}

RomUI.java

package com.example.floatingwindow;import android.os.Build;
import android.text.TextUtils;
import android.util.Log;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;public class RomUtil {private static final String TAG = "RomUtil";public static final String ROM_MIUI = "MIUI";public static final String ROM_EMUI = "EMUI";public static final String ROM_FLYME = "FLYME";public static final String ROM_OPPO = "OPPO";public static final String ROM_SMARTISAN = "SMARTISAN";public static final String ROM_VIVO = "VIVO";public static final String ROM_QIKU = "QIKU";public static final String ROM_LENOVO = "LENOVO";public static final String ROM_SAMSUNG = "SAMSUNG";private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";private static final String KEY_VERSION_EMUI = "ro.build.version.emui";private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";private static final String KEY_VERSION_GIONEE = "ro.gn.sv.version";private static final String KEY_VERSION_LENOVO = "ro.lenovo.lvp.version";private static final String KEY_VERSION_FLYME = "ro.build.display.id";private static final String KEY_EMUI_VERSION_CODE = "ro.build.hw_emui_api_level";private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";private static final String KEY_MIUI_HANDY_MODE_SF = "ro.miui.has_handy_mode_sf";private static final String KEY_MIUI_REAL_BLUR = "ro.miui.has_real_blur";private static final String KEY_FLYME_PUBLISHED = "ro.flyme.published";private static final String KEY_FLYME_FLYME = "ro.meizu.setupwizard.flyme";private static final String KEY_FLYME_ICON_FALG = "persist.sys.use.flyme.icon";private static final String KEY_FLYME_SETUP_FALG = "ro.meizu.setupwizard.flyme";private static final String KEY_FLYME_PUBLISH_FALG = "ro.flyme.published";private static final String KEY_VIVO_OS_NAME = "ro.vivo.os.name";private static final String KEY_VIVO_OS_VERSION = "ro.vivo.os.version";private static final String KEY_VIVO_ROM_VERSION = "ro.vivo.rom.version";public static boolean isEmui() {return check(ROM_EMUI);}public static boolean isMiui() {return check(ROM_MIUI);}public static boolean isVivo() {return check(ROM_VIVO);}public static boolean isOppo() {return check(ROM_OPPO);}public static boolean isFlyme() {return check(ROM_FLYME);}public static boolean isQiku() {return check(ROM_QIKU) || check("360");}public static boolean isSmartisan() {return check(ROM_SMARTISAN);}private static String sName;public static String getName() {if (sName == null) {check("");}return sName;}private static String sVersion;public static String getVersion() {if (sVersion == null) {check("");}return sVersion;}public static boolean check(String rom) {if (sName != null) {return sName.equals(rom);}if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {sName = ROM_MIUI;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {sName = ROM_EMUI;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {sName = ROM_OPPO;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {sName = ROM_VIVO;} else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {sName = ROM_SMARTISAN;} else {sVersion = Build.DISPLAY;if (sVersion.toUpperCase().contains(ROM_FLYME)) {sName = ROM_FLYME;} else {sVersion = Build.UNKNOWN;sName = Build.MANUFACTURER.toUpperCase();}}return sName.equals(rom);}public static String getProp(String name) {String line = null;BufferedReader input = null;try {Process p = Runtime.getRuntime().exec("getprop " + name);input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);line = input.readLine();input.close();} catch (IOException ex) {Log.e(TAG, "Unable to read prop " + name, ex);return null;} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return line;}
}

悬浮窗写好再把弹出框PopupWindow实现就OK了,应用列表可以使用RecyclerView实现。获取应用程序信息使用PackageManager。

popup_app.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="vertical"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerview_menu"android:layout_width="wrap_content"android:layout_height="0dp"android:layout_weight="1"android:layout_gravity="center"/><ImageViewandroid:id="@+id/image_add_menu_item"android:layout_width="30dp"android:layout_height="30dp"android:src="@drawable/add_fill"android:layout_gravity="center"/></LinearLayout>

RecyclerView的子布局

menu_recyclerview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><ImageViewandroid:id="@+id/image_menu_app_icon"android:layout_width="35dp"android:layout_height="35dp"android:layout_marginTop="10dp"android:layout_marginLeft="15dp"android:layout_marginRight="15dp"android:layout_gravity="center"/><TextViewandroid:id="@+id/textview_menu_app_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="15dp"android:textColor="@color/black"android:gravity="center"/></LinearLayout>

实体类ItemMenuAppEntity

ItemMenuAppEntity.java

package com.example.floatingwindow;import android.graphics.drawable.Drawable;public class ItemMenuAppEntity {private String appName;private String appPackage;private String appPackageName;private Drawable appIcon;private boolean displayStatus;public ItemMenuAppEntity(){super();}public ItemMenuAppEntity(String appName,String appPackage,String appPackageName,Drawable appIcon,boolean displayStatus){super();this.appIcon = appIcon;this.appName = appName;this.appPackage = appPackage;this.appPackageName =appPackageName;this.displayStatus = displayStatus;}public String getAppName() {return appName;}public void setAppName(String appName) {this.appName = appName;}public String getAppPackage() {return appPackage;}public void setAppPackage(String appPackage) {this.appPackage = appPackage;}public Drawable getAppIcon() {return appIcon;}public void setAppIcon(Drawable appIcon) {this.appIcon = appIcon;}public boolean isDisplayStatus() {return displayStatus;}public void setDisplayStatus(boolean displayStatus) {this.displayStatus = displayStatus;}public String getAppPackageName() {return appPackageName;}public void setAppPackageName(String appPackageName) {this.appPackageName = appPackageName;}
}

MenuAppRecyclerViewAdapter适配器

MenuAppRecyclerViewAdapter.java

package com.example.floatingwindow;import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import android.widget.PopupWindow;import java.util.List;public class MenuAppRecyclerViewAdapter extends RecyclerView.Adapter<MenuAppRecyclerViewAdapter.ViewHolder> {private List<ItemMenuAppEntity> mItemMenuAppEntityList;private ComponentName mComponentName;private Intent mIntent;private Context mContext;private PopupWindow popupWindow;@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_recyclerview_item,parent,false);ViewHolder viewHolder = new ViewHolder(view);viewHolder.imageIcon.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {int position = viewHolder.getAdapterPosition();ItemMenuAppEntity mItemMenuAppEntity = mItemMenuAppEntityList.get(position);mComponentName = new ComponentName(mItemMenuAppEntity.getAppPackage(),mItemMenuAppEntity.getAppPackageName());mIntent = new Intent();mIntent.setComponent(mComponentName);mContext = view.getContext();mContext.startActivity(mIntent);popupWindow.dismiss();}});return viewHolder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {ItemMenuAppEntity itemMenuAppEntity = mItemMenuAppEntityList.get(position);holder.imageIcon.setBackground(itemMenuAppEntity.getAppIcon());holder.textViewName.setText(itemMenuAppEntity.getAppName());}@Overridepublic int getItemCount() {return mItemMenuAppEntityList.size();}public static class ViewHolder extends RecyclerView.ViewHolder{ImageView imageIcon;TextView textViewName;public ViewHolder(@NonNull View itemView) {super(itemView);imageIcon = (ImageView) itemView.findViewById(R.id.image_menu_app_icon);textViewName = (TextView) itemView.findViewById(R.id.textview_menu_app_name);}}public MenuAppRecyclerViewAdapter(List<ItemMenuAppEntity> mItemMenuAppEntityList,PopupWindow popupWindow){super();this.mItemMenuAppEntityList = mItemMenuAppEntityList;this.popupWindow = popupWindow;}}

PopupWindow.java

package com.example.floatingwindow;import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import java.util.ArrayList;
import java.util.List;public class PopupWindow{private List<ItemMenuAppEntity> mItemMenuAppEntityList =  new ArrayList<>();private Context mContext;public void  popupWindow(Context context,View view) {mContext = context;View inflate = LayoutInflater.from(mContext).inflate(R.layout.popup_app,null);initItem();/*** 创建PopupWindow对象 视图对象, 宽, 高,缺一不可* 第一种:创建对象的时候直接加参数 PopupWindow popupWindow = new PopupWindow(inflate, 200, ViewGroup.LayoutParams.WRAP_CONTENT,true);* 第二种 通过setContentView,setHeight,setWidth 来设置* 宽高可设置固定值或者ViewGroup.LayoutParams.WRAP_CONTENT**/final android.widget.PopupWindow mPopupWindow = new android.widget.PopupWindow(inflate,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);mPopupWindow.setOutsideTouchable(true);//点击弹窗外面关闭弹窗mPopupWindow.setFocusable(true);//设置焦点mPopupWindow.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.corners_background));RecyclerView recyclerView = (RecyclerView) inflate.findViewById(R.id.recyclerview_menu);LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext);recyclerView.setLayoutManager(linearLayoutManager);MenuAppRecyclerViewAdapter menuAppRecyclerViewAdapter = new MenuAppRecyclerViewAdapter(mItemMenuAppEntityList,mPopupWindow);recyclerView.setAdapter(menuAppRecyclerViewAdapter);/*** 获取PopupWindow中View的宽高*/inflate.measure(View.MeasureSpec.UNSPECIFIED,View.MeasureSpec.UNSPECIFIED);int location[] = new int[2];view.getLocationOnScreen(location);/*** 显示popupwidow两种方式* 1.showAsDropDown出现在下方,三个参数:1绑定的控件2.x轴偏移量 3.y轴偏移量* 2.showAtLocation自定义位置 四个参数 1绑定的控件 2.出现的位置 3.x轴偏移量 4.y轴偏移量***/mPopupWindow.showAtLocation(view, Gravity.NO_GRAVITY,location[0]-mPopupWindow.getWidth(),location[1]);}
/*** 通过PackageManage获取程序包名,程序名,图片等*/public void initItem(){mItemMenuAppEntityList.clear();Intent intent = new Intent();intent.setAction(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_LAUNCHER);PackageManager mPackageManager = mContext.getApplicationContext().getPackageManager();List<ResolveInfo> mResolveInfo = mPackageManager.queryIntentActivities(intent,PackageManager.GET_META_DATA);for (ResolveInfo resolveInfo : mResolveInfo){String appName = resolveInfo.loadLabel(mPackageManager).toString();String appPackage = resolveInfo.activityInfo.packageName;String appPackageName = resolveInfo.activityInfo.name;Drawable appIcon = resolveInfo.loadIcon(mPackageManager);ItemMenuAppEntity mItemMenuAppEntity = new ItemMenuAppEntity(appName,appPackage,appPackageName,appIcon,true);mItemMenuAppEntityList.add(mItemMenuAppEntity);}}
}

注意:与手势导航冲突未解决 ,测试时候关闭手势导航。获取应用程序信息需要添加权限

<uses-permissionandroid:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission" />

我之后丰富一下功能就不在这更新了,可以查看我的有道笔记

有道云笔记https://note.youdao.com/s/UpbVjGNq

利用悬浮窗加PopupWindow实现从手机屏幕右边划出应用快捷切换相关推荐

  1. android 代码截图 录屏 浮窗,教你怎么录制手机屏幕视频,安卓手机如何录屏

    原标题:教你怎么录制手机屏幕视频,安卓手机如何录屏 有时候我们或多或少会需要录制手机屏幕上的画面,那么我们如何录制手机屏幕视频呢?其实录制手机屏幕视频还是比较容易的,下面小编便来分享下我录制手机屏幕视 ...

  2. html点击按钮弹出悬浮窗_9种实现点击一个链接弹出一个小窗口的代码

    9 种实现点击一个链接弹出一个小窗口的代码 因为着是一段 javascripts 代码, 所以它们应该放在之间. 是对一些版本低的浏览器起作用,在这些老浏览器中不会将 标签中的代码作为文本显示出来.要 ...

  3. 手机屏幕的横屏竖屏的切换与判断

    在一般情况下,横屏竖屏的切换,会将activity杀死再生成新的,那么会影响用户的体验度.有两种方法,可以解决. (1).直接固定横屏或者竖屏,屏幕不会切换. 在配置清单中: <activity ...

  4. 安卓手机无障碍模式怎么关闭_安卓手机开启无障碍功能与悬浮窗的方法

    怎么开启辅助服务? 使用优Q的功能必须开服辅助服务,辅助服务是安卓官方提供的功能. 打开优Q点击[我的]点击权限设置,开启即可.如果无法快速开启,请参考如下的方法. 部分手机辅助可通过快捷键开启,优Q ...

  5. 电脑桌面悬浮窗记事本_安卓悬浮窗便签记事本怎么添加?求安卓手机悬浮窗便签...

    便签记事本对于我而言,真的是太重要了,如果在日常工作中没有敬业签悬浮窗便签记事本软件的话,我很难想象每天要处理的繁重工作该怎么解决,尤其是在外出办公时,少了这一款悬浮窗便签记事本的话,我的工作就会乱如 ...

  6. Android 悬浮窗基本使用

    很多 iPhone 用户都喜欢打开一项设置,那就是 AssistiveTouch ,我们俗称小白点,它位于整个屏幕之上,就像是漂浮在所有的 App 之上.Android 手机上也有很多应用有这样的东西 ...

  7. Android模仿360动态悬浮窗,像360悬浮窗那样,用WindowManager实现炫酷的悬浮迷你音乐盒(下)...

    悬浮窗 在上一篇文章像360悬浮窗那样,用WindowManager实现炫酷的悬浮迷你音乐盒(上)中我粗粗的向大家介绍了WindowManager和WindowManager.LayoutParams ...

  8. Android 应用弹出悬浮窗

    Android开发者经常遇到应用想弹出悬浮窗的操作,而且有可能还想要高层级弹窗,就像ipone的浮标touch一样.android当然也有类似的悬浮图标,比如前些年我们的流量监控提醒. 这里我们忽略U ...

  9. 像360悬浮窗那样,用WindowManager做一个炫酷的悬浮迷你音乐盒(下)

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 在上一篇文章像360悬浮窗那样,用WindowManager实现炫酷的悬浮迷你音乐盒(上)中我粗粗的向大家介绍了WindowMan ...

最新文章

  1. Unity中sharedMaterials 和 materials
  2. #转载#记录:文献阅读第一利器:文献笔记法(Literature Notes)
  3. linux扩大 boot分区大小,Ubuntu扩大boot空间
  4. python 北上资金_如何查看北向资金? 什么是北向资金 在股市里面,南代表的是HK,北代表的是大陆。北向 = 从南向北流动,那么北向资金就很好理解了,就是HK的... - 雪球...
  5. 《JAVA并发编程的艺术》之 Java并发编程实战
  6. 共享计算机无法打开文件,Windows7局域网无法打开共享文件分析
  7. Ubuntu无网络连接/无网络标识解决方法
  8. js基础——图片切换实例(函数传参)
  9. 配置flashgot+axel
  10. 电子宠物游戏(附C++源码)
  11. DNS记录类型介绍(A记录、MX记录、NS记录等)
  12. 群晖Synology FileStation不显示Share文件夹的解决办法
  13. 多线程编程 ----- 四种同步方法
  14. 3道题彻底搞定:套路解决递归问题
  15. 2021年中国InGaAs+APD接收机市场趋势报告、技术动态创新及2027年市场预测
  16. 哈工大 软件构造Lab1的设计实现
  17. java 制作炸弹人
  18. phpstorm使用总结
  19. 俞敏洪在赢在中国演讲mp3及文字
  20. python重命名文件

热门文章

  1. WebKit之V8技术优化分析
  2. 如何创建一颗完全二叉树(C语言)
  3. 在idea中鼠标光标变成了小手
  4. 笔形式的之字形高低点指标(MT5)
  5. CSS——商品列表子菜单的实现
  6. jsch mysql_Java程序:使用JSch和JDBC通过SSH连接到MySQL
  7. 弘辽科技:小伙退伍网上创业卖特产,教你免费如何开淘宝网店
  8. Python网络爬虫与信息提取(15)—— 新浪网新闻爬虫并分类整理
  9. 中学计算机课 打字教案,计算机课教案.doc
  10. IDEA中解决 maven 包冲突