Android 悬浮窗的实现(类似于360加速球)

            创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口。使用WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。通过(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE); 即可获得WindowManager对象,调用的是WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口。


     


      下面是Android Window悬浮窗的一个小例子,类似于360手机桌面加速球,该加速球可以在桌面拖动,点击后开始转动(此时可以进行如清理垃圾的操作)。


package com.hongri.recyclerview.activity;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;

import com.hongri.recyclerview.MyApplication;
import com.hongri.recyclerview.R;
import com.hongri.recyclerview.fragment.HomeFragment;
import com.hongri.recyclerview.utils.Logger;
import com.hongri.recyclerview.widget.FloatView;

/**
 * @author:zhongyao 
 * @description:主界面Activity
 */
public class MainActivity extends BaseActivity {
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams param;
    private ImageView mLayout;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case FloatView.CLICK:
                    Logger.d("收到click,开始清理垃圾");
                    //属性动画--旋转
                    Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,R.animator.property_animator);
                    animator.setTarget(mLayout);
                    animator.start();

                    break;
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Logger.d("MainActivity--onCreate()");
       
        setContentView(R.layout.activity_main);

        /**
         *Android6.0以上系统增加了权限管理,所以需要添加如下代码,来让用户选择打开桌面浮窗的权限
         */
        if (Build.VERSION.SDK_INT >= 23) {
            if (! Settings.canDrawOverlays(MainActivity.this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent,10);
            }
        }
        showView();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 10) {
            if (Build.VERSION.SDK_INT >= 23) {
                if (!Settings.canDrawOverlays(this)) {
                    // SYSTEM_ALERT_WINDOW permission not granted...
                    Toast.makeText(MainActivity.this, "not granted", Toast.LENGTH_SHORT);
                }
            }
        }
    }

    private void showView(){

        mLayout=new FloatView(getApplicationContext(),mHandler);

        mLayout.setBackgroundResource(R.drawable.sun);

        //获取WindowManager
        mWindowManager=(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
        //设置LayoutParams(全局变量)相关参数
        param = ((MyApplication)getApplication()).getMywmParams();

        param.type=WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;// 系统提示类型,重要(These windows are always on top of application windows)
        param.format=1;
        param.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 表示Window不需要获取焦点
        param.flags = param.flags | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;//可以监听MotionEvent的ACTION_OUTSIDE事件
        param.flags = param.flags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; // 排版限制--即允许在可见的屏幕之外

        param.alpha = 1.0f;

        param.gravity=Gravity.LEFT|Gravity.TOP;   //调整悬浮窗口至左上角
        //以屏幕左上角为原点,设置x、y初始值
        param.x=0;
        param.y=0;

        //设置悬浮窗口长宽数据
        param.width=140;
        param.height=140;

        //显示myFloatView图像
        mWindowManager.addView(mLayout, param);

    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        //在程序退出(Activity销毁)时销毁悬浮窗口
        mWindowManager.removeView(mLayout);
    }
}

package com.hongri.recyclerview.widget;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.ImageView;

import com.hongri.recyclerview.MyApplication;
import com.hongri.recyclerview.utils.Logger;

/**
 * 自定义桌面悬浮image,添加拖动和点击事件
 */
public class FloatView extends ImageView {
    private float mTouchStartX;
    private float mTouchStartY;
    private float x;
    private float y;
    private float beginX,endX,beginY,endY;

    private WindowManager wm=(WindowManager)getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    private WindowManager.LayoutParams wmParams = ((MyApplication)getContext().getApplicationContext()).getMywmParams();
    private Context context;
    private Handler mHandler;
    public static final int CLICK = 1;
    public FloatView(Context context, Handler mHandler) {
        super(context);
        this.context = context;
        this.mHandler = mHandler;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        //获取相对屏幕的坐标,即以屏幕左上角为原点
        x = event.getRawX();
        y = event.getRawY()/*-25*/;   //25是系统状态栏的高度
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //获取相对View的坐标,即以此View左上角为原点
                beginX = endX = mTouchStartX =  event.getX();
                beginY = endY = mTouchStartY =  event.getY();

                Logger.d("ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                updateViewPosition();
                Logger.d("ACTION_MOVE");
                break;

            case MotionEvent.ACTION_UP:
//                updateViewPosition();
                Logger.d("ACTION_UP");

                endX = event.getX();
                endY = event.getY();

                if (endX - beginX == 0 && endY - beginY == 0){
                    //点击事件
                    Logger.d("点击事件");

                    Message msg = Message.obtain();
                    msg.what = CLICK;
                    msg.obj = "点击事件";
                    mHandler.sendMessage(msg);

                }else {
                    //发生了拖拽
                    Logger.d("发生了拖拽");
                }

                mTouchStartX=mTouchStartY=0;
                break;
            case MotionEvent.ACTION_OUTSIDE:
                Logger.d("ACTION_OUTSIDE");
                break;
        }
        return true;
    }

    private void updateViewPosition(){
        //更新浮动窗口位置参数
        wmParams.x=(int)( x-mTouchStartX);
        wmParams.y=(int) (y-mTouchStartY);

        Logger.d("x:"+x+" y:"+y);
        Logger.d("mTouchStartX:"+mTouchStartX+" mTouchStartY:"+mTouchStartY);

        wm.updateViewLayout(this, wmParams);

    }

}

public class MyApplication extends Application {

    private WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
    public WindowManager.LayoutParams getMywmParams(){
        return wmParams;
    }
 }

res/animator文件下的property_animator.xml(属性动画--旋转):

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">

    <set android:ordering="together">
        <objectAnimator
            android:duration="800"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="1080"
            android:valueType="floatType">
       </objectAnimator>
    </set>

</set>


最后在AndroidManifest.xml文件中记得加上如下权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

还有一点需要注意的是在6.0以上的系统中需要用户自己打开“显示悬浮窗”权限才能展示该应用的悬浮窗。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红日666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值