2022-10-12 Android View ImageView实现实体按键的事件监听,悬浮窗的情况的侦听物理按键

一、方法

		View normalView = findViewById(R.id.normal_view);
		// 下面两行代码的顺序不能颠倒
		normalView.setFocusableInTouchMode(true);
		normalView.requestFocus();
		normalView.setOnKeyListener(new View.OnKeyListener() {
			@Override
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				// 需要对action进行判断,否则会收到两次回调
				if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
					Log.i(TAG, "view监听到返回键");
					return true;
				}
				return false;
			}
		});

二、参考别人的

三、悬浮窗的情况的侦听物理按键实例代码

package com.example.suspendedwindow;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Application;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.System;
import java.util.TreeMap;

/**
 * 倒计时60s悬浮窗
 *
 * @author zy
 *
 */
public class MainActivity extends Activity {
    protected static final String TAG = "CountDownActivity";

    private SharedPreferences            alert_window_sp;
    private SharedPreferences.Editor alert_window_editor;
    protected static final int TIME = 1;

    private Context context = MainActivity.this;
    private TextView tv_time;
    private ImageView twenty_four_hour ;
    private static Timer countDown = null;
    private int mValue = 0;
    private int statusBarHeight;// 状态栏高度
    WindowManager wm;
    WindowManager.LayoutParams params ;
    View countDownView;

    private boolean viewAdded = false;// 透明窗体是否已经显示
    Handler post = new Handler();
    LinearLayout    commonCardContainer;
    LinearLayout.LayoutParams params_window;
    int width = 10;
    boolean lock = false ;
    float remeberx = 0;
    float remebery = 0;
    float remebertempx = 0;
    float remebertempy = 0;

    float rangex = 0;
    float rangey = 0;
    int sencond_getx = 0;
    int sencond_gety = 0;

    float downtempx = 0;
    float downtempy = 0;

    boolean lock_temp = false ;
    String LauncherPackageName;
    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        alert_window_sp   = getSharedPreferences("ALERT_WINDOW_DATA", Context.MODE_PRIVATE);
        alert_window_editor   = alert_window_sp.edit();
        Log.i(TAG, "getFrontPackageName= " + getFrontPackageName( getApplicationContext()));
        String packageName = "";
        if (needPermissionForBlocking(getApplicationContext())) {
            //如果用户没有授权,引导用户去设置页面授权
            Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
            //在service中开启activity需要为intent添加FLAG_ACTIVITY_NEW_TASK的flag
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        } else {
            //得到包名
            packageName = getTopPackage();
        }

        LauncherPackageName = getLauncherPackageName(getApplicationContext());
        Log.i(TAG, "top app = " + packageName+ " LauncherPackageName:"+LauncherPackageName);

        Log.i(TAG, " getTopActivity():" + getTopActivity());

    }

    private String getTopActivity() {
        String className = null;
        ActivityManager mActivityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> list = mActivityManager.getRunningTasks(1);
        if (!list.isEmpty() && list.get(0) != null && list.get(0).topActivity != null) {
            className = list.get(0).topActivity.getClassName();
        }
        return className;
    }

    public String getLauncherPackageName(Context context)
    {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        final ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0);
        if(res.activityInfo == null)
        {
            return "";
        }
        //如果是不同桌面主题,可能会出现某些问题,这部分暂未处理
        if(res.activityInfo.packageName.equals("android"))
        {
            return "";
        }else
        {
            return res.activityInfo.packageName;
        }
    }
    public static String get_test_time(String str,int day_number) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date day = sdf.parse(str);

        long ms = day.getTime() - day_number*24*3600*1000L+3*3600*1000L+25*60*1000L;
        Date prevDay = new Date(ms);
        System.out.println(sdf.format(prevDay));
        return sdf.format(prevDay);
    }
    public static String current_time()
    {
        Date date = new Date();
        String strDateFormat = "yyyy-MM-dd HH:mm";
        SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
        System.out.println("current time:"+sdf.format(date));
        return sdf.format(date);
    }

    /**
     * 点击显示悬浮窗
     *
     * @param
     */
    @SuppressLint("WrongConstant")
    public void show(View v) {
        wm = (WindowManager) getApplicationContext().getSystemService(
                WINDOW_SERVICE); // 注意:这里必须是全局的context
        // 判断UI控件是否存在,存在则移除,确保开启任意次应用都只有一个悬浮窗
        if (countDownView != null) {
            wm.removeView(countDownView);
        }

/*
        params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
*/
        params = new WindowManager.LayoutParams();
        params.width  = WindowManager.LayoutParams.WRAP_CONTENT;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;

        //params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        params.flags =  WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        // 系统级别的窗口
        params.type =  WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

                //| WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
      //  params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        // 居中显示
       // params.gravity = Gravity.CENTER;
        //params.gravity = Gravity.RIGHT|Gravity.BOTTOM; //悬浮窗开始在右下角显示
        params.gravity = Gravity.LEFT | Gravity.TOP;
        // 设置背景透明
        params.format = PixelFormat.TRANSPARENT;

        countDownView = new View(getApplicationContext()); // 不依赖activity的生命周期
        countDownView = View.inflate(getApplicationContext(),
                R.layout.countdown_weight, null);


        tv_time = (TextView) countDownView.findViewById(R.id.tv_time);

        twenty_four_hour = (ImageView) countDownView.findViewById(R.id.img);


        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();
        lp.width  = alert_window_sp.getInt("window_width",500);
        lp.height = alert_window_sp.getInt("window_height", 250);
        twenty_four_hour.setLayoutParams(lp);
/*
        try{
             tv_time.setText(get_test_time(current_time(),1));
        }  catch (ParseException e) {
             e.printStackTrace();
        }
 */
        wm.addView(countDownView, params);


        viewAdded = true;

        countDownView.setFocusableInTouchMode(true);
        countDownView.requestFocus();
        countDownView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                // TODO Auto-generated method stub
                Log.i(TAG, "setOnKeyListener onKey keyCode:"+keyCode);
                if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
                {
                    lock =(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) ? true:false;
                    if(lock)
                        params.flags = 0 ;
                    else
                        params.flags =  WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

                    Toast.makeText(MainActivity.this, lock ? "Locked":"Unlocked", Toast.LENGTH_SHORT).show();
                    refresh();
                    return true;
                }

                return false;
            }
            public boolean onKeyDown(int keyCode, KeyEvent msg) {
                Log.i(TAG, "setOnKeyListener onKeyDown");
                return true;
            }
        });

        /**
         * 监听窗体移动事件
         */
        countDownView.setOnTouchListener(new View.OnTouchListener() {
            float[] temp = new float[] { 0f, 0f };

            @RequiresApi(api = Build.VERSION_CODES.Q)
            public boolean onTouch(View v, MotionEvent event) {
                int PointerCount;
                PointerCount = event.getPointerCount();
               // Log.i(TAG, "PointerCount:"+PointerCount);
              // Log.i(TAG, "temp[0]:" + temp[0]+" temp[1]:" + temp[1] + " X:" + event.getX()+
                    //    " Y:"+event.getY() + " RawX:"+event.getRawX() + " RawY:"+event.getRawY()+" PointerCount:"+PointerCount);

                int eventaction = event.getAction();
                switch (eventaction & MotionEvent.ACTION_MASK) {
                    case MotionEvent.ACTION_DOWN: // 按下事件,记录按下时手指在悬浮窗的XY坐标值
                        temp[0] = event.getX();
                        temp[1] = event.getY();
                        remeberx = event.getRawX();
                        remebery = event.getRawY();
                        Log.i(TAG, " MotionEvent.ACTION_DOWN "+temp[0]+temp[1]+"\n");

                        break;
                    case MotionEvent.ACTION_POINTER_DOWN:
                        //屏幕上已经有一个点被按住了 第二个点被按下时触发该事件
                        Log.i(TAG, "MotionEvent.ACTION_POINTER_DOWN \n" + "downtempx:" + downtempx + " downtempy:" + downtempy);
                        lock_temp = true;
                        break;
                    case MotionEvent.ACTION_POINTER_UP:
                        Log.i(TAG, "MotionEvent.ACTION_POINTER_UP \n");
                        break;
                    case MotionEvent.ACTION_UP:
                        Log.i(TAG, "MotionEvent.ACTION_UP \n");
                        if(PointerCount == 1) {

                        }
                        lock_temp = false;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        // Log.i(TAG, "MotionEvent.ACTION_MOVE event.getActionIndex():"+event.getActionIndex()+"\n");
                       // Log.i(TAG, "remeberx:"+remeberx+"remebery:"+remebery+
                        //     " event.getRawX():"+event.getRawX()+" event.getRawY():"+event.getRawY()+"\n");

                         if(PointerCount == 2)
                         {
                             rangex = (float)((event.getRawX() - remeberx) * 0.3) ;
                             rangey = (float)((event.getRawY() - remebery) * 0.3) ;

                           //  RelativeLayout twenty_four_hour = (RelativeLayout) countDownView.findViewById(R.id.twenty_four_hour);
                             RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();

                             if((rangex > 1)||(rangex < -1)) {
                                 lp.width = (int) (lp.width + rangex);
                                 remeberx = event.getRawX();
                             }
                             if((rangey > 1)||(rangey < -1)) {
                                 lp.height = (int) (lp.height + rangey);
                                 remebery = event.getRawY();
                             }
                             alert_window_editor.putInt("window_width", lp.width);
                             alert_window_editor.putInt("window_height", lp.height);
                             alert_window_editor.commit();
                             twenty_four_hour.setLayoutParams(lp);
                             return false;
                         }
                        if(lock || lock_temp)
                            return true;

                        if(PointerCount == 1)
                            refreshView((int) (event.getRawX() - temp[0]), (int) (event.getRawY() - temp[1]));

                        break;
                }
                return true;
            }
        });

/*
        cancle.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                Log.e(TAG, "取消倒计时");
                wm.removeView(countDownView);
                countDownView = null;
                countDown.cancel();
                mValue = 999;
            }
        });
*/
        // 添加倒计时功能
        countDown = new Timer();
        countDown.schedule(new TimerTask() {
            @Override
            public void run() {
                String FrontPackageName ="";
                mValue++;
                post.post(drawCount);
                FrontPackageName = getFrontPackageName(getApplicationContext());
                Log.i(TAG, "FrontPackageName = " + FrontPackageName);
                Log.i(TAG, " getCurrentPkgName:" + getCurrentPkgName(getApplicationContext()));
                if(FrontPackageName != null) {
                    //if(mValue > 20)
                     //   twenty_four_hour.setVisibility(View.INVISIBLE);

                    if (FrontPackageName.contains(LauncherPackageName) && mValue > 100)
                    {
                        mValue = 0;
                    }

                }
                Log.i(TAG, " mValue:"+mValue);

                if (mValue == 0) {
                    // 执行关机操作(这里可以使任意其他操作,根据自己的需求)
                    Log.e(TAG, "close");
                    wm.removeView(countDownView);
                    countDownView = null;
                    // 取消定时
                    countDown.cancel();
                    finish();
                }
            }
        }, 0, 500);

       // refreshView(295, 949);
        statusBarHeight = 0 ;

        refreshView(alert_window_sp.getInt("paramsx",250),alert_window_sp.getInt("paramsy",250));

        finish();
    }

    private void refreshView(int x, int y) {
        // 状态栏高度不能立即取,不然得到的值是0
        if (statusBarHeight == 0) {
            View rootView = countDownView.getRootView();
            Rect r = new Rect();
            rootView.getWindowVisibleDisplayFrame(r);
            statusBarHeight = r.top;
        }

        params.x = x;
        // y轴减去状态栏的高度,因为状态栏不是用户可以绘制的区域,不然拖动的时候会有跳动
        params.y = y - statusBarHeight;// STATUS_HEIGHT;

        Log.i(TAG, " x:"+x+" y:"+y+" params.x:"+params.x+" params.y:" +params.y +" statusBarHeight:"+statusBarHeight);
        alert_window_editor.putInt("paramsx", params.x);
        alert_window_editor.putInt("paramsy", params.y);
        alert_window_editor.commit();
        //Log.i(TAG, "MotionEvent.ACTION_UP SAVE ALERT WINDOW DATA params.x params.y\n");
        refresh();

    }

    /**
     * 添加悬浮窗或者更新悬浮窗 如果悬浮窗还没添加则添加 如果已经添加则更新其位置
     */
    private void refresh() {
        // 如果已经添加了就只更新view
        if (viewAdded) {
            wm.updateViewLayout(countDownView, params);
        } else {
            wm.addView(countDownView, params);
            viewAdded = true;
        }
    }

    /**
     * 模拟其他操作
     * @param view
     */
    public void other(View view) {
        Toast.makeText(context, "别的操作", Toast.LENGTH_SHORT).show();
        ImageView twenty_four_hour = (ImageView) findViewById(R.id.img);
        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();
        lp.width = lp.width + 1;
        twenty_four_hour.setLayoutParams(lp);
        Log.i(TAG, "lp.width:"+lp.width+ " topMargin:"+lp.topMargin);
        // startActivity(new Intent(context, NewActivity.class));
    }

    Runnable drawCount = new Runnable() {
        @Override
        public void run() {
            //tv_time.setText(Integer.toString(mValue));
        }
    };

    /**
     * 获得top activity的包名
     * @return
     */
    public String getTopPackage(){
        long ts = System.currentTimeMillis();
        UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService(Context.USAGE_STATS_SERVICE);
        List<UsageStats> usageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts-1000, ts);
        if (usageStats == null || usageStats.size() == 0) {//如果为空则返回""
            return "";
        }
        Collections.sort(usageStats, new RecentUseComparator());//mRecentComp = new RecentUseComparator()
        return usageStats.get(0).getPackageName();
    }
    static class RecentUseComparator implements Comparator<UsageStats> {
        @Override
        public int compare(UsageStats lhs, UsageStats rhs) {
            return (lhs.getLastTimeUsed() > rhs.getLastTimeUsed()) ? -1 : (lhs.getLastTimeUsed() == rhs.getLastTimeUsed()) ? 0 : 1;
        }
    }

    public static boolean needPermissionForBlocking(Context context) {
        try {
            PackageManager packageManager = context.getPackageManager();
            ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
            AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
            return (mode != AppOpsManager.MODE_ALLOWED);
        } catch (PackageManager.NameNotFoundException e) {
            return true;
        }
    }
    public String getFrontPackageName(Context context) {
        String topPackageName = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
            long time = System.currentTimeMillis();
            // We get usage stats for the last 10 seconds
            List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
            // Sort the stats by the last time used
            if (stats != null) {
                SortedMap<Long, UsageStats> mySortedMap = new TreeMap<>();
                for (UsageStats usageStats : stats) {
                    mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
                }
                if (mySortedMap != null && !mySortedMap.isEmpty()) {
                    topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
                    return topPackageName;
                }
            }
        }
        return null;
    }

    /**
     * 注意: getRunningAppProcesses()方法在5.0开始,就只返回自身应用的进程,所以只能判断自身进程状态,
     * 如果是400,返回为null,不能拿到当前栈顶Activity的包名
     *
     * @param context
     * @return
     */
    private static String getCurrentPkgName(Context context) {
        // 5x系统以后利用反射获取当前栈顶activity的包名.
        ActivityManager.RunningAppProcessInfo currentInfo = null;
        Field field = null;
        int startTaskToFront = 2;
        String pkgName = null;
        try {
            // 通过反射获取进程状态字段.
            field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
        } catch (Exception e) {
            e.printStackTrace();
        }
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List appList = am.getRunningAppProcesses();
        ActivityManager.RunningAppProcessInfo app;
        for (int i = 0; i < appList.size(); i++) {
            //ActivityManager.RunningAppProcessInfo app : appList
            app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
            //表示前台运行进程.
            if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                Integer state = null;
                try {
                    // 反射调用字段值的方法,获取该进程的状态.
                    state = field.getInt(app);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 根据这个判断条件从前台中获取当前切换的进程对象
                if (state != null && state == startTaskToFront) {
                    currentInfo = app;
                    break;
                }
            }
        }
        if (currentInfo != null) {
            pkgName = currentInfo.processName;
        }
        return pkgName;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "倒计时结束");
    };
}

 四、参考文章

Android View监听按键返回事件 - 掘金 (juejin.cn)

猜你喜欢

转载自blog.csdn.net/qq_37858386/article/details/127287706
今日推荐