仿ios的悬浮窗查看最近应用怎么不能用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了仿ios的悬浮窗查看最近应用怎么不能用?相关的知识,希望对你有一定的参考价值。

参考技术A 在一些场合里,我们使用悬浮窗会有很大的便利,比如ios系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。
我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗。
代码如下:
在这之前,我们需要在manifest中申请权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1
并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行
小悬浮窗的界面代码float_normal_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ll_float_normal"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_show_control_view"
android:layout_gravity="center"
android:background="@drawable/float_bg"
android:layout_width="65dp"
android:orientation="vertical"
android:alpha="0.3"
android:layout_height="65dp" >
</ImageView>
</LinearLayout>
大悬浮窗的界面代码float_window_big.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="/apk/res/android"
android:id="@+id/big_window_layout"
android:layout_width="200dip"
android:layout_height="100dip"
android:background="@android:color/holo_green_dark"
android:orientation="vertical"
>
<Button
android:id="@+id/close"
android:layout_width="100dip"
android:layout_height="40dip"
android:layout_gravity="center_horizontal"
android:layout_marginTop="12dip"
android:text="关闭悬浮窗"
/>
<Button
android:id="@+id/back"
android:layout_width="100dip"
android:layout_height="40dip"
android:layout_gravity="center_horizontal"
android:text="主页"
/>
</LinearLayout>
入口activity(MainActivity ):
public class MainActivity extends Activity
MyWindowManager myWindowManager;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
if(!Settings.canDrawOverlays(getApplicationContext()))
//启动Activity让用户授权
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent,100);


setContentView(R.layout.activity_main);
myWindowManager = MyWtijian.scykxy.cn#indowManager.getInstance();
myWindowManager.createNormalView(this.getApplicationContext());


悬浮窗管理器MyWindowManager:
public class MyWindowManager
private FloatNormalView normalView;
private Context mContext;
private static MyWindowManager instance;
private WindowManager windowManager;
private MyWindowManager()

public static MyWindowManager getInstance()
if (instance == null)
instance = new MyWindowManager();
return instance;

private WindowManager getWindowManager(Context context)
if (windowManager == null)
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mContext=context;
return windowManager;

/**
* 判断小悬浮窗是否存在
*
* @return
*/
public boolean isNormalViewExists()
return normalView != null;

/**
* 创建小型悬浮窗
*/
public void createNormalView(Context context)
if (normalView == null)
normalView = new FloatNormalView(context);


/**
* 移除悬浮窗
*
* @param context
*/
public void removeNormalView(Context context)
if (normalView != null)
windowManager.removeView(normalView);
normalView = null;


FloatWindowBigView bigWindow;
private WindowManager.LayoutParams bigWindowParams;
public void createBigWindow(Context context)
WindowManager windowManager = getWindowManager(context);
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeight = windowManager.getDefaultDisplay().getHeight();
if (bigWindow == null)
bigWindow = new FloatWindowBigView(context);
if (bigWindowParams == null)
bigWindowParams = new WindowManager.LayoutParams();
bigWindowParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2;
bigWindowParams.y = screenHeight / 2 - FloatWindowBigView.viewHeight / 2;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
bigWindowParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

else
bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;

bigWindowParams.format = PixelFormat.RGBA_8888;
bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
bigWindowParams.width = FloatWindowBigView.viewWidth;
bigWindowParams.height = FloatWindowBigView.viewHeight;

windowManager.addView(bigWindow, bigWindowParams);


public void removeBigWindow(Context context)
if (bigWindow != null)
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(bigWindow);
bigWindow = null;



小悬浮窗FloatNormalView:
public class FloatNormalView extends LinearLayout implements View.OnTouchListener
private final static String TAG="FloatNormalView";
/**
* 记录小悬浮窗的宽度
*/
public static int viewWidth;
/**
* 记录小悬浮窗的高度
*/
public static int viewHeight;
/**
* 记录系统状态栏的高度
*/
private static int statusBarHeight;
/**
* 用于更新小悬浮窗的位置
*/
private WindowManager windowManager;
/**
* 小悬浮窗的参数
*/
private WindowManager.LayoutParams mParams;
/**
* 记录当前手指位置在屏幕上的横坐标值
*/
private float xInScreen,xInitScreen;
/**
* 记录当前手指位置在屏幕上的纵坐标值
*/
private float yInScreen,yInitScreen;
/**
* 记录手指按下时在屏幕上的横坐标的值
*/
private float xDownInScreen;
/**
* 记录手指按下时在屏幕上的纵坐标的值
*/
private float yDownInScreen;
/**
* 记录手指按下时在小悬浮窗的View上的横坐标的值
*/
private float xInView;
/**
* 记录手指按下时在小悬浮窗的View上的纵坐标的值
*/
private float yInView;
public FloatNormalView(Context context)
super(context);
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
View view = findViewById(R.id.ll_float_normal);
viewWidth = view.getLayoutParams().width;
viewHeight = view.getLayoutParams().height;
initLayoutParams();

OnClickListener circleClickListener=new OnClickListener()
@Override
public void onClick(View view)
MyWindowManager.getInstance().createBigWindow(getContext());
MyWindowManager.getInstance().removeNormalView(getContext());

;
/**
* 初始化参数
*/
private void initLayoutParams()
//屏幕宽高
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeight = windowManager.getDefaultDisplay().getHeight();
mParams = new WindowManager.LayoutParams();
// FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
// FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
//悬浮窗默认显示的位置
mParams.gravity = Gravity.START | Gravity.TOP;
//指定位置
mParams.x = screenWidth - viewWidth * 2;
mParams.y = screenHeight / 2 + viewHeight * 2;
//悬浮窗的宽高
mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.format = PixelFormat.TRANSPARENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
mParams.type =WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

else
mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
windowManager.addView(this, mParams);

@Override
public boolean onTouchEvent(MotionEvent event)
Log.i(TAG,"hsz--->onTouchEvent:"+event.getAction());
switch (event.getAction())
case MotionEvent.ACTION_DOWN:
// 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
xInView =xInitScreen= event.getX();
yInView =yInitScreen= event.getY();
xDownInScreen = event.getRawX();
yDownInScreen = event.getRawY() - getStatusBarHeight();
xInScreen = event.getRawX();
yInScreen = event.getRawY() - getStatusBarHeight();
break;
case MotionEvent.ACTION_MOVE:
xInScreen = event.getRawX();
yInScreen = event.getRawY() - getStatusBarHeight();
// 手指移动的时候更新小悬浮窗的位置
updateViewPosition();
break;
case MotionEvent.ACTION_UP:
// 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
if (xDownInScreen == xInScreen && yDownInScreen == yInScreen)
openOrCloseControlView();

Float distanceX=event.getX()-xInitScreen;
Float distanceY=event.getY()-yInitScreen;
if(distanceX==0 && distanceY==0)
MyWindowManager.getInstance().createBigWindow(getContext());
MyWindowManager.getInstance().removeNormalView(getContext());

break;
default:
break;

return true;

/**
* 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。
*
* @param params 小悬浮窗的参数
*/
public void setParams(WindowManager.LayoutParams params)
mParams = params;

/**
* 更新小悬浮窗在屏幕中的位置。
*/
private void updateViewPosition()
mParams.x = (int) (xInScreen - xInView);
mParams.y = (int) (yInScreen - yInView);
windowManager.updateViewLayout(this, mParams);

/**
* 用于获取状态栏的高度。
*
* @return 返回状态栏高度的像素值。
*/
private int getStatusBarHeight()
if (statusBarHeight == 0)
try
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object o = c.newInstance();
Field field = c.getField("status_bar_height");
int x = (Integer) field.get(o);
statusBarHeight = getResources().getDimensionPixelSize(x);
catch (Exception e)
e.printStackTrace();


return statusBarHeight;

@Override
public boolean onTouch(View v, MotionEvent event)
return false;


大悬浮窗FloatWindowBigView:
public class FloatWindowBigView extends LinearLayout
/**
* 记录大悬浮窗的宽度
*/
public static int viewWidth;
/**
* 记录大悬浮窗的高度
*/
public static int viewHeight;
public FloatWindowBigView(final Context context)
super(context);
LayoutInflater.from(context).inflate(R.layout.float_window_big, this);
View view = findViewById(R.id.big_window_layout);
viewWidth = view.getLayoutParams().width;
viewHeight = view.getLayoutParams().height;
Button close = (Button) findViewById(R.id.close);
Button back = (Button) findViewById(R.id.back);
close.setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
// 点击关闭悬浮窗的时候
MyWindowManager.getInstance().removeBigWindow(context);
MyWindowManager.getInstance().createNormalView(context);

);
back.setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
// 点击主页的时候,移除大悬浮窗,创建小悬浮窗
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_HOME);
context.startActivity(intent);
MyWindowManager.getInstance().removeBigWindow(context);
MyWindowManager.getInstance().createNormalView(context);

);

Android仿腾讯手机管家实现桌面悬浮窗小火箭发射的动画效果

功能分析: 
1、小火箭游离在activity之外,不依附于任何activity,不管activity是否开启,不影响小火箭的代码逻辑,所以小火箭的代码逻辑是要写在服务中; 
2、小火箭挂载在手机窗体之上; 
3、小火箭喷射火焰,两种图片轮流展示的动画集; 
4、小火箭可以拖拽到任意位置; 
5、小火箭拖拽到指定区域才可以触发发射效果; 
6、小火箭喷射尾气动画效果;

 

功能实现:

定义小火箭的服务,实现自定义Toast的创建,自定义Toast加载相应的布局文件,然后把自定义Toast挂载到手机窗体之上;

public class RocketService extends Service {

    // 手机窗体布局的管理者
    private WindowManager mWindowManager;
    // 手机窗体的布局
    private WindowManager.LayoutParams mParams;
    // 展示小火箭的自定义布局
    private View mToastRocketView;
    // 展示小火箭的ImageView
    private ImageView mRocketImage;
    // 手机窗体的宽度
    private int mWindowWidth;
    // 手机窗体的高度
    private int mWindowHeight;
    // 消息传递机制
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mParams.y = (Integer) msg.obj;
            mWindowManager.updateViewLayout(mToastRocketView, mParams);
        }
    };

    @Override
    public void onCreate() {
        mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        // 获取手机屏幕的宽高值
        mWindowWidth = mWindowManager.getDefaultDisplay().getWidth();
        mWindowHeight = mWindowManager.getDefaultDisplay().getHeight();
        mParams = new WindowManager.LayoutParams();
        // 服务启动,打开自定义Toast的控件
        showRocketView();
        // 拖拽小火箭到任意位置
        dragRocket();
        super.onCreate();
    }

    /**
     * 拖拽小火箭到任意位置
     */
    private void dragRocket() {
        mToastRocketView.setOnTouchListener(new View.OnTouchListener() {
            private int startX;
            private int startY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int moveX = (int) event.getRawX();
                        int moveY = (int) event.getRawY();
                        // 两个方向上所移动的距离值
                        int disX = moveX - startX;
                        int disY = moveY - startY;

                        mParams.x = mParams.x + disX;
                        mParams.y = mParams.y + disY;

                        if (mParams.x < 0) {
                            mParams.x = 0;
                        }

                        if (mParams.y < 0) {
                            mParams.y = 0;
                        }

                        if (mParams.x > mWindowManager.getDefaultDisplay().getWidth() - v.getWidth()) {
                            mParams.x = mWindowManager.getDefaultDisplay().getWidth() - v.getWidth();
                        }

                        if (mParams.y > mWindowManager.getDefaultDisplay().getHeight() - 21 - v.getHeight()) {
                            mParams.y = mWindowManager.getDefaultDisplay().getHeight() - 21 - v.getHeight();
                        }

                        // 更新小火箭的坐标位置X和Y值
                        mWindowManager.updateViewLayout(mToastRocketView, mParams);

                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();

                        break;
                    case MotionEvent.ACTION_UP:
                        // 小火箭拖拽到手机屏幕下方的中间时,触发小火箭发射
                        if (mParams.x > mWindowWidth / 2 - 150 && mParams.x < mWindowWidth / 2 - mToastRocketView.getWidth() / 2 + 50
                                && mParams.y > mWindowHeight - mToastRocketView.getHeight() - 25) {
                            // 小火箭发射升空
                            launchRocket();
                            Intent intent = new Intent(RocketService.this, SmokeBackActivity.class);
                            // 服务中开启activity,需要设置任务栈
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                            startActivity(intent);
                        }
                        break;
                }
                return false;
            }
        });
    }

    /**
     * 小火箭发射升空
     */
    private void launchRocket() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int disY = mWindowHeight / 5;
                for (int i = 0; i < 6; i++) {
                    int height = mWindowHeight - i * disY;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 子线程不能改变主线程中的UI的变化,因此,由消息机制告知主线程进行改变,并携带相应的值
                    Message msg = Message.obtain();
                    msg.obj = height;
                    mHandler.sendMessage(msg);
                }
            }
        }).start();
    }

    /**
     * 显示小火箭的自定义View
     */
    private void showRocketView() {
        // 自定义Toast
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        // 修改完左上角对齐
        mParams.gravity = Gravity.LEFT + Gravity.TOP;
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        mParams.format = PixelFormat.TRANSLUCENT;
        mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        // 加载ToastRocketView显示效果的布局文件
        mToastRocketView = View.inflate(this, R.layout.toast_rocket_view, null);
        // 窗体布局中加入自定义的展示小火箭的View
        mWindowManager.addView(mToastRocketView, mParams);
        mRocketImage = (ImageView) mToastRocketView.findViewById(R.id.rocket_image);
        // 获取动画,并开启动画
        AnimationDrawable animDraw = (AnimationDrawable) mRocketImage.getBackground();
        animDraw.start();
    }

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

    @Override
    public void onDestroy() {
        if (mWindowManager != null && mToastRocketView != null) {
            mWindowManager.removeView(mToastRocketView);
        }
        super.onDestroy();
    }
}

 

mWindowManager是所有桌面悬浮窗的管理器,这里用于添加我们自定义的小火箭的Toast,然后获取小火箭火焰喷射的动画集,并开启动画效果。上述代码中为mToastRocketView添加了触摸事件,用于监听小火箭的拖拽事件,在监听拖拽事件的同时,若小火箭被拖动到指定区域,则触发小火箭的发射效果;

小火箭所在的布局文件:

<?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="match_parent"
    android:orientation="vertical">

    <!--背景为小火箭火焰喷射的动画集-->
    <ImageView
        android:id="@+id/rocket_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/rocket_anim_list"/>

</LinearLayout>

 

小火箭火焰喷射的动画集:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">

    <!--小火箭火焰喷射的动画集-->
    <item android:drawable="@drawable/rocket_launch_1" android:duration="200" />
    <item android:drawable="@drawable/rocket_launch_2" android:duration="200" />

</animation-list>

 

小火箭发射时的尾气喷射动画效果,这里为了获得好的效果,需要在AndroidManifest.xml中配置活动的主题为透明、无标题和全屏;

public class SmokeBackActivity extends Activity {
    // 尾气喷射上部图片所在的ImageView
    private ImageView mSmokeTopImageView;
    // 尾气喷射下部图片所在的ImageView
    private ImageView mSmokeBottomImageView;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 尾气喷射动画结束后关闭活动
            finish();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_smoke_back);
        // 初始化布局文件
        initView();
    }

    /**
     * 初始化布局文件
     */
    private void initView() {
        mSmokeTopImageView = (ImageView) findViewById(R.id.smoke_top);
        mSmokeBottomImageView = (ImageView) findViewById(R.id.smoke_bottom);
        // 尾气喷射设置动画效果并开启
        AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
        alphaAnimation.setDuration(500);
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF,
                0.5f, Animation.RELATIVE_TO_SELF, 1f);
        scaleAnimation.setDuration(500);
        // 动画集中添加缩放和透明动画
        AnimationSet animationSet = new AnimationSet(true);
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(scaleAnimation);
        mSmokeTopImageView.startAnimation(animationSet);
        mSmokeBottomImageView.startAnimation(alphaAnimation);
        // 1秒之后关闭当期活动
        mHandler.sendEmptyMessageDelayed(0, 1000);
    }
}

 

所对应的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/smoke_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/smoke_m"
        android:layout_alignParentBottom="true"/>

    <ImageView
        android:id="@+id/smoke_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/smoke_bottom"
        android:background="@drawable/smoke_t" />

</RelativeLayout>

 

AndroidManifest.xml中添加相应的权限:

<!--全局弹出对话框需要添加权限-->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

 

效果如下所示: 

 

主活动中只是设置俩按钮,一个开启服务,一个关闭服务,这里不再赘述,欢迎大家去GitHub上下载代码;有什么问题也可以评论交流一下;

代码地址:GitHub地址

 

转: https://blog.csdn.net/u010347226/article/details/77677908

 

以上是关于仿ios的悬浮窗查看最近应用怎么不能用?的主要内容,如果未能解决你的问题,请参考以下文章

Android 桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

悬浮窗是啥,怎么开启悬浮窗?

华为mate9的悬浮窗怎么操作

Android 悬浮窗如何能让它和他的的下层一起响应触摸事件?

华为悬浮窗会挡住下面的点击

WPF如何实现悬浮窗和主窗口的切换?