Android utilise WindowManager pour implémenter le glissement de fenêtre, avec des effets de bord d'ancrage automatiques supplémentaires
le code
Fichier xml de l'interface principale
Interface principale 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>
fichier xml de fenêtre
vue de la fenêtre 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>
Effet
Activité principale
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);
}
}
}
}