讓你的應(yīng)用"飄"起來(lái)!Android懸浮窗+自動(dòng)吸邊效果實(shí)戰(zhàn)
先搞張"通行證"(權(quán)限篇)
在AndroidManifest里掛個(gè)牌子
<!-- 告訴系統(tǒng):我要開(kāi)懸浮窗啦! -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
這就像在游樂(lè)園門(mén)口掛了個(gè)"VIP通道"的牌子~
向用戶(hù)申請(qǐng)"特別通行證"
// 檢查是不是Android 6.0以上的"高級(jí)游樂(lè)園"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 看看通行證有沒(méi)有過(guò)期
if (!Settings.canDrawOverlays(this)) {
// 申請(qǐng)權(quán)限
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1001);
} else {
showFloatingWindow(); // 亮出我們的懸浮窗!
}
}
圖片
創(chuàng)建會(huì)"跑"的懸浮窗(核心實(shí)現(xiàn))
懸浮窗基礎(chǔ)配置
// 窗口管家(WindowManager)登場(chǎng)
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// 給懸浮窗穿件"衣服"
ImageView floatView = new ImageView(this);
floatView.setImageResource(R.drawable.ic_float);
// 懸浮窗的"身份證信息"
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : // Android 8.0+用這個(gè)
WindowManager.LayoutParams.TYPE_PHONE, // 老版本用這個(gè)
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // 別搶焦點(diǎn),做個(gè)安靜的美窗
PixelFormat.TRANSLUCENT // 半透明效果更高級(jí)
);
// 初始位置:屏幕左上角+向下偏移100像素
params.gravity = Gravity.START | Gravity.TOP;
params.x = 0;
params.y = 100;
// 把懸浮窗"放"到屏幕上
windowManager.addView(floatView, params);
圖片
讓?xiě)腋〈?動(dòng)"起來(lái)
floatView.setOnTouchListener(new View.OnTouchListener() {
private int startX, startY;
private float touchX, touchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 手指按下
startX = params.x;
startY = params.y;
touchX = event.getRawX(); // 記錄觸點(diǎn)X
touchY = event.getRawY(); // 記錄觸點(diǎn)Y
return true;
case MotionEvent.ACTION_MOVE: // 手指滑動(dòng)
// 計(jì)算新位置
params.x = startX + (int)(event.getRawX() - touchX);
params.y = startY + (int)(event.getRawY() - touchY);
windowManager.updateViewLayout(floatView, params);
return true;
case MotionEvent.ACTION_UP: // 手指松開(kāi)
autoAttachToEdge(); // 觸發(fā)吸邊效果
return true;
}
return false;
}
});
磁吸邊效果實(shí)現(xiàn)(魔法時(shí)刻)
private void autoAttachToEdge() {
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int viewCenterX = params.x + floatView.getWidth()/2;
// 判斷離哪邊近
if(viewCenterX < screenWidth/2) {
params.x = 0; // 吸到左邊緣
} else {
params.x = screenWidth - floatView.getWidth(); // 吸到右邊緣
}
// 添加彈性動(dòng)畫(huà)
ValueAnimator animator = ValueAnimator.ofInt(params.x, targetX);
animator.addUpdateListener(animation -> {
params.x = (int) animation.getAnimatedValue();
windowManager.updateViewLayout(floatView, params);
});
animator.setDuration(300).start();
}
圖片
重要注意事項(xiàng)
1. 版本適配:Android 8.0+要用TYPE_APPLICATION_OVERLAY
類(lèi)型
2. 內(nèi)存管理:記得在onDestroy
里移除視圖!
3. 用戶(hù)體驗(yàn):拖動(dòng)時(shí)可以考慮增加半透明效果
4. 權(quán)限提示:優(yōu)雅地引導(dǎo)用戶(hù)開(kāi)啟權(quán)限
快來(lái)打造屬于你的"桌面小精靈"吧!代碼在手,創(chuàng)意我有,讓你的應(yīng)用瞬間擁有靈魂~ ??
源碼:https://github.com/Reathin/Sample-Android/tree/master/module_float