[Android] Use WindowManager to realize window dragging, and add automatic docking edge effect

Android uses WindowManager to implement window dragging, with additional automatic docking edge effects

the code

Main interface xml file

Main interface activity_main

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/alert_window_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="弹窗"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.13"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.068" />
</androidx.constraintlayout.widget.ConstraintLayout>

window xml file

window's view window_layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:background="@drawable/window_border"
    android:orientation="vertical">

    <TextView
        android:id="@+id/window_bar"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:text="提示"
        android:gravity="center_vertical"
        android:textSize="20dp"
        android:textColor="@color/white"
        android:background="@color/blue"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="18dp"
        android:padding="8dp"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center"
        android:padding="5dp"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">
            <Button
                android:id="@+id/window_remove"
                android:layout_width="80dp"
                android:layout_height="40dp"
                android:text="取消"
                android:textStyle="bold"
                android:background="@color/dark_white"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">

            <Button
                android:layout_width="80dp"
                android:layout_height="40dp"
                android:background="@color/blue"
                android:textColor="@color/white"
                android:textStyle="bold"
                android:text="确定"/>
        </LinearLayout>

    </LinearLayout>

</LinearLayout>

Effect
insert image description here

MainActivity

package com.lwh.windowmanagertest;

import androidx.appcompat.app.AppCompatActivity;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    
    

    boolean removed = false;

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 申请系统权限
        requestWindowPermission();

        // 导入窗口
        View windowLayout = LayoutInflater.from(this).inflate(R.layout.window_layout, null);
        // 窗口的标题
        TextView windowBar = windowLayout.findViewById(R.id.window_bar);
        // 窗口的取消和确定按钮
        Button windowRemove = windowLayout.findViewById(R.id.window_remove);
        // 弹出窗口按钮
        Button alertWindowButton = findViewById(R.id.alert_window_button);

        // 获取WindowManager
        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        // 创建LayoutParams
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();


        // 定义Window类型
        layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;;
        layoutParams.format = PixelFormat.RGBA_8888;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE    // 设置窗口不接受输入焦点
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; // 设置锁屏时也显示窗口

        layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; // window的宽度
        layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;// window的高度

        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        // window的位置
        layoutParams.x = 0;
        layoutParams.y = 100;
        // 往window中添加内容
        windowManager.addView(windowLayout, layoutParams);

        // 屏幕宽度(应用显示部分,不包括系统顶部状态栏)
        int screenWidth = getResources().getDisplayMetrics().widthPixels;

        // 设置拖动窗口标题移动
        windowBar.setOnTouchListener(new View.OnTouchListener() {
    
    
            float oldX;  // 上一次鼠标点击时的X坐标
            float oldY;  // 上一次鼠标点击时的Y坐标
            float windowX;  // 弹出窗口的X坐标
            float windowY; // 弹出窗口的Y坐标

            @SuppressLint("Recycle")
            @Override
            public boolean onTouch(View v, MotionEvent event) {
    
    
                switch (event.getAction()) {
    
    
                    case MotionEvent.ACTION_MOVE:
                        // event.getRawX()-oldX为偏移量
                        // 新位置 = 原始位置 + 偏移量
                        layoutParams.x = (int) (windowX + event.getRawX()-oldX);
                        layoutParams.y = (int) (windowY +  event.getRawY()-oldY);
                        windowManager.updateViewLayout(windowLayout, layoutParams);
                        break;
                    // 鼠标弹起时判断窗口X轴的位置,然后将其贴边
                    case MotionEvent.ACTION_UP:
                        double halfScreenWidth = screenWidth / 2.0;
                        // 如果X轴坐标大于屏幕宽度的一半,则将其置于右边,否则左边
                        if (layoutParams.x >= halfScreenWidth){
    
    
                            // layoutParams.x = screenWidth - windowLayout.getMeasuredWidth();
                            // 执行动画
                            ValueAnimator valueAnimator = ValueAnimator.ofInt(layoutParams.x, screenWidth - windowLayout.getMeasuredWidth());
                            valueAnimator.setDuration(200);
                            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    
    
                                @Override
                                public void onAnimationUpdate(ValueAnimator animation) {
    
    
                                    int x = (int) animation.getAnimatedValue();
                                    layoutParams.x = (int) x;
                                    windowManager.updateViewLayout(windowLayout, layoutParams);
                                }
                            });
                            valueAnimator.start();
                        }else{
    
    
                            // layoutParams.x = 0;
                            // 执行动画
                            ValueAnimator valueAnimator = ValueAnimator.ofInt(layoutParams.x, 0);
                            valueAnimator.setDuration(200);
                            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    
    
                                @Override
                                public void onAnimationUpdate(ValueAnimator animation) {
    
    
                                    int x = (int) animation.getAnimatedValue();
                                    layoutParams.x = (int) x;
                                    windowManager.updateViewLayout(windowLayout, layoutParams);
                                }
                            });

                            valueAnimator.start();
                        }
                        // windowManager.updateViewLayout(windowLayout, layoutParams);
                    default:
                        oldX = event.getRawX();
                        oldY = event.getRawY();
                        // 计算移动后的位置(左上角的坐标)
                        // windowX = event.getRawX() - event.getX();
                        // windowX = event.getRawY() - event.getY();
                        windowX = layoutParams.x;
                        windowY = layoutParams.y;
                        break;
                }
                return true;
            }
        });
        // 点击取消删除窗口
        windowRemove.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                windowManager.removeView(windowLayout);
                removed = true;
            }
        });

        alertWindowButton.setOnClickListener(new View.OnClickListener() {
    
    

            @Override
            public void onClick(View v) {
    
    
                if (removed){
    
    
                    windowManager.addView(windowLayout, layoutParams);
                    removed = false;
                }else{
    
    
                    windowManager.removeView(windowLayout);
                    removed = true;
                }
            }
        });
    }

    private void requestWindowPermission() {
    
    
        //android 6.0或者之后的版本需要发一个intent让用户授权
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    
            if (!Settings.canDrawOverlays(getApplicationContext())) {
    
    
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivity(intent);
                // startActivityForResult(intent, 100);
            }
        }
    }
}

Effect

insert image description here

Guess you like

Origin blog.csdn.net/weixin_44500303/article/details/128498175