Android 實(shí)現(xiàn)可任意拖動(dòng)的懸浮窗功能(類似懸浮球)
最近開發(fā)項(xiàng)目中,有個(gè)在屏幕上任意拖動(dòng)的懸浮窗功能,其實(shí)就是利用 WindowManager的api來完成這個(gè)需求,具體的實(shí)現(xiàn)的功能如下:
1.自定義view
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.LinearLayout;
import com.xinrui.recordscreen.R;
import java.lang.reflect.Field;
/**
*
*/
public class RecordScreenView extends LinearLayout implements View.OnClickListener{
private WindowManager mWindowManager;
private WindowManager.LayoutParams mLayoutParams;
private long mLastDownTime;
private float mLastDownX;
private float mLastDownY;
private boolean mIsLongTouch;
private boolean mIsTouching;
private float mTouchSlop;
private final static long LONG_CLICK_LIMIT = 20;
private final static int TIME_COUNT = 0;
private int mStatusBarHeight;
private int mCurrentMode,time=0;
private final static int MODE_NONE = 0x000;
private final static int MODE_MOVE = 0x001;
private int mOffsetToParent;
private int mOffsetToParentY;
private Context mContext;
public RecordScreenView(Context context) {
super(context);
this.mContext=context;
mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
initView();
}
private void initView() {
View view = inflate(getContext(), R.layout.layout_ball, this);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mCurrentMode = MODE_NONE;
recordtime(0);
mStatusBarHeight = getStatusBarHeight();
mOffsetToParent = dip2px(25);
mOffsetToParentY = mStatusBarHeight + mOffsetToParent;
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mIsTouching = true;
mLastDownTime = System.currentTimeMillis();
mLastDownX = event.getX();
mLastDownY = event.getY();
postDelayed(new Runnable() {
@Override
public void run() {
if (isLongTouch()) {
mIsLongTouch = true;
}
}
}, LONG_CLICK_LIMIT);
break;
case MotionEvent.ACTION_MOVE:
if (!mIsLongTouch && isTouchSlop(event)) {
return true;
}
if (mIsLongTouch && (mCurrentMode == MODE_NONE || mCurrentMode == MODE_MOVE)) {
mLayoutParams.x = (int) (event.getRawX() - mOffsetToParent);
mLayoutParams.y = (int) (event.getRawY() - mOffsetToParentY);
mWindowManager.updateViewLayout(RecordScreenView.this, mLayoutParams);//不斷刷新懸浮窗的位置
mCurrentMode = MODE_MOVE;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mIsTouching = false;
if (mIsLongTouch) {
mIsLongTouch = false;
}
mCurrentMode = MODE_NONE;
break;
}
return true;
}
});
}
private boolean isLongTouch() {
long time = System.currentTimeMillis();
if (mIsTouching && mCurrentMode == MODE_NONE && (time - mLastDownTime >= LONG_CLICK_LIMIT)) {
return true;
}
return false;
}
/**
* 判斷是否是輕微滑動(dòng)
*
* @param event
* @return
*/
private boolean isTouchSlop(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if (Math.abs(x - mLastDownX) < mTouchSlop && Math.abs(y - mLastDownY) < mTouchSlop) {
return true;
}
return false;
}
public void setLayoutParams(WindowManager.LayoutParams params) {
mLayoutParams = params;
}
/**
* 獲取通知欄高度
*
* @return
*/
private int getStatusBarHeight() {
int statusBarHeight = 0;
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object o = c.newInstance();
Field field = c.getField("status_bar_height");
int x = (Integer) field.get(o);
statusBarHeight = getResources().getDimensionPixelSize(x);
} catch (Exception e) {
e.printStackTrace();
}
return statusBarHeight;
}
public int dip2px(float dip) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dip, getContext().getResources().getDisplayMetrics()
);
}
}
2.添加windowManager添加view
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
/**
* Created by wangxiandeng on 2016/11/25.
*/
public class FloatWindowManager {
private static RecordScreenView mBallView;
private static WindowManager mWindowManager;
public static void addBallView(Context context) {
if (mBallView == null) {
WindowManager windowManager = getWindowManager(context);
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeight = windowManager.getDefaultDisplay().getHeight();
mBallView = new RecordScreenView(context);
LayoutParams params = new LayoutParams();
params.x = screenWidth/2;
params.y = screenHeight/2+150;
params.width = LayoutParams.WRAP_CONTENT;
params.height = LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.type = LayoutParams.TYPE_APPLICATION_OVERLAY;
params.format = PixelFormat.RGBA_8888;
params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE;
mBallView.setLayoutParams(params);
windowManager.addView(mBallView, params);
}
}
public static void removeBallView(Context context) {
if (mBallView != null) {
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(mBallView);
mBallView = null;
}
}
private static WindowManager getWindowManager(Context context) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
return mWindowManager;
}
}
3.Acitivity中調(diào)用
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import com.xinrui.recordscreen.view.FloatWindowManager;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 23) {
//設(shè)置中請(qǐng)求開啟懸浮窗權(quán)限
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Toast.makeText(this, MainActivity.this.getResources().getString(R.string.open_float), Toast.LENGTH_SHORT).show();
}else{
initView();
}
}
}
private void initView() {
FloatWindowManager.addBallView(MainActivity.this);
finish();
}
}
5.AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xinrui.recordscreen">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>//懸浮窗權(quán)限
<application
android:allowBackup="true"
android:icon="@drawable/recording_screen_nor"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name="com.xinrui.recordscreen.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</manifest>
總結(jié)
到此這篇關(guān)于Android 實(shí)現(xiàn)可任意拖動(dòng)的懸浮窗功能(類似懸浮球)的文章就介紹到這了,更多相關(guān)Android任意拖動(dòng)的懸浮窗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ANDROID中自定義對(duì)話框AlertDialog使用示例
這篇文章主要為大家詳細(xì)介紹了Android中自定義對(duì)話框AlertDialog使用示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android 彈出Dialog時(shí)隱藏狀態(tài)欄和底部導(dǎo)航欄的方法
這篇文章主要介紹了Android 彈出Dialog時(shí)隱藏狀態(tài)欄和底部導(dǎo)航欄的實(shí)例代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07
詳解Android6.0運(yùn)行時(shí)權(quán)限管理
自從Android6.0發(fā)布以來,在權(quán)限上做出了很大的變動(dòng),不再是之前的只要在manifest設(shè)置就可以任意獲取權(quán)限,而是更加的注重用戶的隱私和體驗(yàn)。本文詳細(xì)介紹了Android6.0運(yùn)行時(shí)權(quán)限管理。需要的朋友一起來看下吧2016-12-12
Android仿iOS側(cè)滑退出當(dāng)前界面功能
這篇文章主要為大家詳細(xì)介紹了Android仿iOS側(cè)滑退出當(dāng)前界面功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android串口開發(fā)之使用JNI實(shí)現(xiàn)ANDROID和串口通信詳解
這篇文章主要給大家介紹了關(guān)于Android串口開發(fā)之使用JNI實(shí)現(xiàn)ANDROID和串口通信的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
去掉RecycleView或者ListView上下滑動(dòng)陰影的方法
下面小編就為大家分享一篇去掉RecycleView或者ListView上下滑動(dòng)陰影的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
android9.0 默認(rèn)apk權(quán)限添加方法
本文給大家分享android9.0 默認(rèn)apk權(quán)限添加方法,默認(rèn)賦予全部權(quán)限,根據(jù)包名賦予權(quán)限,通過default-permissions-google.xml的方式實(shí)現(xiàn),文中通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-06-06
Android采用GET方法進(jìn)行網(wǎng)絡(luò)傳值
這篇文章主要為大家詳細(xì)介紹了Android采用GET方法進(jìn)行網(wǎng)絡(luò)傳值的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12

