Android——自由拖動並顯示文字的懸浮框實現
阿新 • • 發佈:2019-01-01
專案中需要實現一個狀態顯示的懸浮框,要求可以設定兩種模式:拖動模式和不可拖動模式。
實現效果圖如下:
實現步驟:
1.首先要設定該懸浮框的基本屬性:
比較重要的點是要注意設定flags,我這裡提供了兩種flags以供切換:/** * 顯示彈出框 * * @param context */ @SuppressWarnings("WrongConstant") public static void showPopupWindow(final Context context, String showtxt) { if (isShown) { return; } isShown = true; // 獲取WindowManager mWindowManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); mView = setUpView(context, showtxt); params = new WindowManager.LayoutParams(); // 型別,系統提示以及它總是出現在應用程式視窗之上。 params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; // 設定flag int flags = canTouchFlags; // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 如果設定了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,彈出的View收不到Back鍵的事件 params.flags = flags; // 不設定這個彈出框的透明遮罩顯示為黑色 params.format = PixelFormat.TRANSLUCENT; // FLAG_NOT_TOUCH_MODAL不阻塞事件傳遞到後面的視窗 // 設定 FLAG_NOT_FOCUSABLE 懸浮視窗較小時,後面的應用圖示由不可長按變為可長按 // 不設定這個flag的話,home頁的劃屏會有問題 params.width = LayoutParams.WRAP_CONTENT; params.height = LayoutParams.WRAP_CONTENT; params.gravity = Gravity.TOP; mWindowManager.addView(mView, params); }
第一種是可觸控不可聚焦模式,第二種是不可觸控不可聚焦模式。其他的flags可以從api中查閱。private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
2.設定懸浮框的拖動監聽事件:
private static View setUpView(final Context context, String showtxt) { View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow, null); TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop); showTv.setText(showtxt); rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop); rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() { private float lastX; //上一次位置的X.Y座標 private float lastY; private float nowX; //當前移動位置的X.Y座標 private float nowY; private float tranX; //懸浮窗移動位置的相對值 private float tranY; @Override public boolean onTouch(View v, MotionEvent event) { boolean ret = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 獲取按下時的X,Y座標 lastX = event.getRawX(); lastY = event.getRawY(); ret = true; break; case MotionEvent.ACTION_MOVE: // 獲取移動時的X,Y座標 nowX = event.getRawX(); nowY = event.getRawY(); // 計算XY座標偏移量 tranX = nowX - lastX; tranY = nowY - lastY; params.x += tranX; params.y += tranY; //更新懸浮窗位置 mWindowManager.updateViewLayout(mView, params); //記錄當前座標作為下一次計算的上一次移動的位置座標 lastX = nowX; lastY = nowY; break; case MotionEvent.ACTION_UP: break; } return ret; } });
這裡要在down的時候記錄座標,move事件中使用修改params座標進行移動。
3.設定懸浮框文字屬性:
public static void setShowTxt(String txt) {
try {
TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);
showTv.setText(txt);
mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
Log.d(TAG, "setShowTxt: 更新懸浮框錯誤");
e.printStackTrace();
if(e.getMessage().contains("not attached to window manager")){
mWindowManager.addView(mView, params);
}
}
}
4.更新懸浮框圖片顯示:
public static void setShowImg(Bitmap bitmap) {
try {
ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);
showImg.setImageBitmap(bitmap);
mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
Log.d(TAG, "setShowTxt: 更新懸浮框錯誤");
e.printStackTrace();
if(e.getMessage().contains("not attached to window manager")){
mWindowManager.addView(mView, params);
}
}
}
介紹完畢,整個類都封裝好了,程式碼如下:
/**
* 懸浮窗工具類
* created by Pumpkin at 17/3/28
*/
public class WindowsUitlity {
private static String TAG = WindowsUitlity.class.getSimpleName();
private static WindowManager mWindowManager = null;
private static WindowManager.LayoutParams params;
public static Boolean isShown = false;
private static View mView = null;
/**
* 顯示彈出框
*
* @param context
*/
@SuppressWarnings("WrongConstant")
public static void showPopupWindow(final Context context, String showtxt) {
if (isShown) {
return;
}
isShown = true;
// 獲取WindowManager
mWindowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context, showtxt);
params = new WindowManager.LayoutParams();
// 型別,系統提示以及它總是出現在應用程式視窗之上。
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
// 設定flag
int flags = canTouchFlags;
// | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 如果設定了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,彈出的View收不到Back鍵的事件
params.flags = flags;
// 不設定這個彈出框的透明遮罩顯示為黑色
params.format = PixelFormat.TRANSLUCENT;
// FLAG_NOT_TOUCH_MODAL不阻塞事件傳遞到後面的視窗
// 設定 FLAG_NOT_FOCUSABLE 懸浮視窗較小時,後面的應用圖示由不可長按變為可長按
// 不設定這個flag的話,home頁的劃屏會有問題
params.width = LayoutParams.WRAP_CONTENT;
params.height = LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.TOP;
mWindowManager.addView(mView, params);
}
private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
/**
* 設定是否可響應點選事件
*
* @param isTouchable
*/
public static void setTouchable(boolean isTouchable) {
if (isTouchable) {
params.flags = canTouchFlags;
} else {
params.flags = notTouchFlags;
}
mWindowManager.updateViewLayout(mView, params);
}
/**
* 隱藏彈出框
*/
public static void hidePopupWindow() {
if (isShown && null != mView) {
mWindowManager.removeView(mView);
isShown = false;
}
}
public static void setShowTxt(String txt) {
try {
TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);
showTv.setText(txt);
mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
Log.d(TAG, "setShowTxt: 更新懸浮框錯誤");
e.printStackTrace();
if(e.getMessage().contains("not attached to window manager")){
mWindowManager.addView(mView, params);
}
}
}
public static void setShowImg(Bitmap bitmap) {
try {
ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);
showImg.setImageBitmap(bitmap);
mWindowManager.updateViewLayout(mView, params);
}catch (Exception e){
Log.d(TAG, "setShowTxt: 更新懸浮框錯誤");
e.printStackTrace();
if(e.getMessage().contains("not attached to window manager")){
mWindowManager.addView(mView, params);
}
}
}
static RelativeLayout rl_drag_showinpop;
private static View setUpView(final Context context, String showtxt) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow,
null);
TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop);
showTv.setText(showtxt);
rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop);
rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() {
private float lastX; //上一次位置的X.Y座標
private float lastY;
private float nowX; //當前移動位置的X.Y座標
private float nowY;
private float tranX; //懸浮窗移動位置的相對值
private float tranY;
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean ret = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 獲取按下時的X,Y座標
lastX = event.getRawX();
lastY = event.getRawY();
ret = true;
break;
case MotionEvent.ACTION_MOVE:
// 獲取移動時的X,Y座標
nowX = event.getRawX();
nowY = event.getRawY();
// 計算XY座標偏移量
tranX = nowX - lastX;
tranY = nowY - lastY;
params.x += tranX;
params.y += tranY;
//更新懸浮窗位置
mWindowManager.updateViewLayout(mView, params);
//記錄當前座標作為下一次計算的上一次移動的位置座標
lastX = nowX;
lastY = nowY;
break;
case MotionEvent.ACTION_UP:
break;
}
return ret;
}
});
return view;
}
}