Android开发笔记 自定义悬浮窗

WindowManager

在前面《 Android开发笔记(六十六)自定义对话框》中,我们提到每个页面都是一个Window窗口,许多的Window对象需要一个管家来打理,这个管家我们称之为WindowManager窗口管理。在手机屏幕上新增或删除页面窗口,都可以归结为WindowManager的操作,下面是该管理类的常用方法说明:
getDefaultDisplay : 获取默认的显示屏信息。通常用该方法获取屏幕分辨率,详情参见《 Android开发笔记(三)屏幕分辨率》
addView : 往窗口添加视图。第二个参数为WindowManager.LayoutParams对象。
updateViewLayout : 更新指定视图的布局参数。第二个参数为WindowManager.LayoutParams对象。
removeView : 往窗口移除指定视图。


下面是窗口布局参数WindowManager.LayoutParams的常用属性说明:
format : 窗口的像素点格式。取值见PixelFormat类中的常量定义,一般取值PixelFormat.RGBA_8888。
type : 窗口的显示类型,常用的类型说明如下:
--TYPE_SYSTEM_ALERT : 系统警告提示。
--TYPE_SYSTEM_ERROR : 系统错误提示。
--TYPE_SYSTEM_OVERLAY : 页面顶层提示。
--TYPE_SYSTEM_DIALOG : 系统对话框。
--TYPE_STATUS_BAR : 状态栏
--TYPE_TOAST : 短暂通知Toast
flags : 窗口的行为准则,常用的标志位如下说明(对于悬浮窗来说,一般只需设置FLAG_NOT_FOCUSABLE):
--FLAG_NOT_FOCUSABLE : 不能抢占焦点,即不接受任何按键或按钮事件。
--FLAG_NOT_TOUCHABLE : 不接受触摸屏事件。悬浮窗一般不设置该标志,因为一旦设置该标志,将无法拖动悬浮窗。
--FLAG_NOT_TOUCH_MODAL : 当窗口允许获得焦点时(即没有设置FLAG_NOT_FOCUSALBE标志),仍然将窗口之外的按键事件发送给后面的窗口处理。否则它将独占所有的按键事件,而不管它们是不是发生在窗口范围之内。
-- : 
--FLAG_LAYOUT_IN_SCREEN : 允许窗口占满整个屏幕。
--FLAG_LAYOUT_NO_LIMITS : 允许窗口扩展到屏幕之外。
--FLAG_WATCH_OUTSIDE_TOUCH : 如果设置了FLAG_NOT_TOUCH_MODAL标志,则当按键动作发生在窗口之外时,将接收到一个MotionEvent.ACTION_OUTSIDE事件。
alpha : 窗口的透明度,取值为0-1。
gravity : 取值同View的setGravity方法。
x : 窗口左上角的X坐标。
y : 窗口左上角的Y坐标。
width : 窗口的宽度。
height : 窗口的高度。


静态悬浮窗

悬浮窗有点类似对话框,它们都是独立于Activity页面的窗口,但是悬浮窗又有一些与众不同的特性,例如:
1、悬浮窗是可以拖动的,对话框则不能;
2、悬浮窗不妨碍用户触摸窗外的区域,对话框则不让用户操作框外的控件;
3、悬浮窗独立于Activity页面,即当页面退出后,悬浮窗仍停留在屏幕上;而对话框与Activity页面是共存关系,一旦页面退出则对话框也消失了;


基于悬浮窗的以上特性,我们要实现窗口的悬浮效果,就不仅仅是调用WindowManager的addView方法那么简单了,而是需要做一系列的自定义处理,具体步骤如下:
1、在AndroidManifest.xml中声明系统窗口权限,即增加下面这句:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

2、在自定义的悬浮窗控件中,要设置触摸监听器,并根据用户的手势滑动来相应调整窗口位置,以实现悬浮窗的拖动功能;
3、合理设置悬浮窗的窗口参数,主要是把窗口参数的显示类型设置为TYPE_SYSTEM_ALERT或者TYPE_SYSTEM_ERROR,另外要设置标志位FLAG_NOT_FOCUSABLE
4、在构造悬浮窗实例时,要传入Application的上下文Context,这是为了保证即使退出Activity,也不会关闭悬浮窗。因为Application对象在app运行过程中是始终存在着的,而Activity对象只在打开页面时有效,一旦退出页面则Activity的上下文就立刻回收(这会导致依赖于该上下文的悬浮窗也一块被回收了)。


下面是一个静态悬浮窗的效果截图:

 

下面是自定义悬浮窗的示例代码:

import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
 
public class FloatView extends View {
    private final static String TAG = "FloatView";
 
    private Context mContext;
    private WindowManager wm;
    private static WindowManager.LayoutParams wmParams;
    public View mContentView;
    private float mRelativeX;
    private float mRelativeY;
    private float mScreenX;
    private float mScreenY;
    private boolean bShow = false;
 
    public FloatView(Context context) {
        super(context);
        wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        if (wmParams == null) {
            wmParams = new WindowManager.LayoutParams();
        }
        mContext = context;
    }
    
    public void setLayout(int layout_id) {
        mContentView = LayoutInflater.from(mContext).inflate(layout_id, null);
        mContentView.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                mScreenX = event.getRawX();
                mScreenY = event.getRawY();
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mRelativeX = event.getX();
                    mRelativeY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    updateViewPosition();
                    break;
                case MotionEvent.ACTION_UP:
                    updateViewPosition();
                    mRelativeX = mRelativeY = 0;
                    break;
                }
                return true;
            }
        });
    }
 
    private void updateViewPosition() {
        wmParams.x = (int) (mScreenX - mRelativeX);
        wmParams.y = (int) (mScreenY - mRelativeY);
        wm.updateViewLayout(mContentView, wmParams);
    }
    
    public void show() {
        if (mContentView != null) {
            wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
            wmParams.format = PixelFormat.RGBA_8888;
            wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            wmParams.alpha = 1.0f;
            wmParams.gravity = Gravity.LEFT | Gravity.TOP;
            wmParams.x = 0;
            wmParams.y = 0;
            wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
            wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
            // 显示自定义悬浮窗口
            wm.addView(mContentView, wmParams);
            bShow = true;
        }
    }
 
    public void close() {
        if (mContentView != null) {
            wm.removeView(mContentView);
            bShow = false;
        }
    }
    
    public boolean isShow() {
        return bShow;
    }
 
}


下面是打开/关闭悬浮窗的页面代码:

import com.example.exmfloat.widget.FloatView;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
 
public class StaticActivity extends Activity implements OnClickListener {
    
    private FloatView mFloatView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static);
 
        Button btn_static_open = (Button) findViewById(R.id.btn_static_open);
        Button btn_static_close = (Button) findViewById(R.id.btn_static_close);
        btn_static_open.setOnClickListener(this);
        btn_static_close.setOnClickListener(this);
        
        mFloatView = new FloatView(MainApplication.getInstance());
        mFloatView.setLayout(R.layout.float_static);
    }
 
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_static_open) {
            if (mFloatView!=null && mFloatView.isShow()==false) {
                mFloatView.show();
            }
        } else if (v.getId() == R.id.btn_static_close) {
            if (mFloatView!=null && mFloatView.isShow()==true) {
                mFloatView.close();
            }
        }
    }
 
}


下面是自定义Application的代码例子:

import android.app.Application;
 
public class MainApplication extends Application {
 
    private static MainApplication mApp;
    
    public static MainApplication getInstance() {
        return mApp;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        mApp = this;
    }
    
}


动态悬浮窗

    在实际开发中,悬浮窗的展示内容是变化的,毕竟一个内容不变的悬浮窗对用户来说没什么用处。具体的应用例子有很多,比如说时钟、天气、实时流量、股市指数等等,下面就以实时流量与股市指数两个例子,来详细说明动态悬浮窗的实际应用。
   

    要想实时刷新悬浮窗,这得通过服务Service来实现,所以动态悬浮窗要在Service服务中创建和更新,页面只负责启动/停止服务。对于手机的实时流量,可以通过TrafficStats类的相关方法计算得到,该类的详细说明参见《 Android开发笔记(七十九)资源与权限校验》


下面是实时流量悬浮窗的效果截图:

 

下面是实时流量悬浮窗的页面代码:

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
 
import com.example.exmfloat.service.TrafficService;
 
public class TrafficActivity extends Activity implements OnClickListener {
    private final static String TAG = "TrafficActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_traffic);
 
        Button btn_traffic_open = (Button) findViewById(R.id.btn_traffic_open);
        Button btn_traffic_close = (Button) findViewById(R.id.btn_traffic_close);
        btn_traffic_open.setOnClickListener(this);
        btn_traffic_close.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_traffic_open) {
            Intent intent = new Intent(this, TrafficService.class);
            intent.putExtra("type", TrafficService.OPEN);
            startService(intent);
        } else if (v.getId() == R.id.btn_traffic_close) {
            Intent intent = new Intent(this, TrafficService.class);
            intent.putExtra("type", TrafficService.CLOSE);
            startService(intent);
        }
    }
 
}

下面是实时流量悬浮窗的服务代码:

import com.example.exmfloat.MainApplication;
import com.example.exmfloat.R;
import com.example.exmfloat.util.FlowUtil;
import com.example.exmfloat.widget.FloatView;
 
import android.app.Service;
import android.content.Intent;
import android.net.TrafficStats;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.TextView;
 
public class TrafficService extends Service {
    private final static String TAG = "TrafficService";
    public static int OPEN = 0;
    public static int CLOSE = 1;
    
    private long curRx;
    private long curTx;
    private FloatView mFloatView;
    private TextView tv_traffic;
    private final int delayTime = 2000;
 
    private Handler mHandler = new Handler();
    private Runnable mRefresh = new Runnable() {
        public void run() {
            if (mFloatView != null && mFloatView.isShow() == true &&
                    (TrafficStats.getTotalRxBytes()>curRx || TrafficStats.getTotalTxBytes()>curTx)) {
                long a = ((TrafficStats.getTotalRxBytes() - curRx) + (TrafficStats
                        .getTotalTxBytes() - curTx)) / 2;
                String desc = String.format("当前流量: %s/S", FlowUtil.BToShowStringNoDecimals(a));
                tv_traffic.setText(desc);
                curRx = TrafficStats.getTotalRxBytes();
                curTx = TrafficStats.getTotalTxBytes();
            }
            mHandler.postDelayed(this, delayTime);
        }
    };
 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        if (mFloatView == null) {
            mFloatView = new FloatView(MainApplication.getInstance());
            mFloatView.setLayout(R.layout.float_traffic);
            tv_traffic = (TextView) mFloatView.mContentView.findViewById(R.id.tv_traffic);
        }
        curRx = TrafficStats.getTotalRxBytes();
        curTx = TrafficStats.getTotalTxBytes();
        mHandler.postDelayed(mRefresh, 0);
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            int type = intent.getIntExtra("type", OPEN);
            if (type == OPEN) {
                if (mFloatView != null && mFloatView.isShow() == false) {
                    mFloatView.show();
                }
            } else if (type == CLOSE) {
                if (mFloatView != null && mFloatView.isShow() == true) {
                    mFloatView.close();
                }
                stopSelf();
            }
        }
 
        return super.onStartCommand(intent, flags, startId);
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRefresh);
    }
 
}

对于股市指数的展示,可以通过调用财经网站的实时指数查询接口得到,比如新浪财经与腾讯财经均提供了上证指数与深圳成指的查询接口。


下面是实时股指悬浮窗的效果截图:

 

下面是实时股指悬浮窗的页面代码:

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
 
import com.example.exmfloat.service.StockService;
 
public class StockActivity extends Activity implements OnClickListener {
    private final static String TAG = "StockActivity";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stock);
 
        Button btn_stock_open = (Button) findViewById(R.id.btn_stock_open);
        Button btn_stock_close = (Button) findViewById(R.id.btn_stock_close);
        btn_stock_open.setOnClickListener(this);
        btn_stock_close.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_stock_open) {
            Intent intent = new Intent(this, StockService.class);
            intent.putExtra("type", StockService.OPEN);
            startService(intent);
        } else if (v.getId() == R.id.btn_stock_close) {
            Intent intent = new Intent(this, StockService.class);
            intent.putExtra("type", StockService.CLOSE);
            startService(intent);
        }
    }
 
}

下面是实时股指悬浮窗的服务代码:

import com.example.exmfloat.MainApplication;
import com.example.exmfloat.R;
import com.example.exmfloat.http.HttpReqData;
import com.example.exmfloat.http.HttpRespData;
import com.example.exmfloat.http.HttpUrlUtil;
import com.example.exmfloat.widget.FloatView;
 
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.TextView;
 
public class StockService extends Service {
    private final static String TAG = "StockService";
    public static int OPEN = 0;
    public static int CLOSE = 1;
 
    private FloatView mFloatView;
    private TextView tv_sh_stock, tv_sz_stock;
    private final int delayTime = 5000;
 
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //上证指数,3019.9873,-5.6932,-0.19,1348069,14969598
            String desc = (String) msg.obj;
            String[] array = desc.split(",");
            String stock = array[1];
            float distance = Float.parseFloat(array[2]);
            String range = array[3];
            String text = String.format("%s  %s%%", stock, range);
            int type = msg.what;
            if (type == SHANGHAI) {
                tv_sh_stock.setText(text);
                if (distance > 0) {
                    tv_sh_stock.setTextColor(Color.RED);
                } else {
                    tv_sh_stock.setTextColor(Color.GREEN);
                }
            } else if (type == SHENZHEN) {
                tv_sz_stock.setText(text);
                if (distance > 0) {
                    tv_sz_stock.setTextColor(Color.RED);
                } else {
                    tv_sz_stock.setTextColor(Color.GREEN);
                }
            }
        }
    };
    
    private Runnable mRefresh = new Runnable() {
        @Override
        public void run() {
            if (mFloatView != null && mFloatView.isShow() == true ) {
                new StockThread(SHANGHAI).start();
                new StockThread(SHENZHEN).start();
            }
            mHandler.postDelayed(this, delayTime);
        }
    };
    
    private static int SHANGHAI = 0;
    private static int SHENZHEN = 1;
    private class StockThread extends Thread {
        private int mType;
        public StockThread(int type) {
            mType = type;
        }
        
        @Override
        public void run() {
            HttpReqData req_data = new HttpReqData();
            if (mType == SHANGHAI) {
                req_data.url = "http://hq.sinajs.cn/list=s_sh000001";
            } else if (mType == SHENZHEN) {
                req_data.url = "http://hq.sinajs.cn/list=s_sz399001";
            }
            HttpRespData resp_data = HttpUrlUtil.getData(req_data);
            //var hq_str_s_sh000001="上证指数,3019.9873,-5.6932,-0.19,1348069,14969598";
            String desc = resp_data.content;
            Message msg = Message.obtain();
            msg.what = mType;
            msg.obj = desc.substring(desc.indexOf("\"")+1, desc.lastIndexOf("\""));
            mHandler.sendMessage(msg);
        }
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        if (mFloatView == null) {
            mFloatView = new FloatView(MainApplication.getInstance());
            mFloatView.setLayout(R.layout.float_stock);
            tv_sh_stock = (TextView) mFloatView.mContentView.findViewById(R.id.tv_sh_stock);
            tv_sz_stock = (TextView) mFloatView.mContentView.findViewById(R.id.tv_sz_stock);
        }
        mHandler.postDelayed(mRefresh, 0);
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            int type = intent.getIntExtra("type", OPEN);
            if (type == OPEN) {
                if (mFloatView != null && mFloatView.isShow() == false) {
                    mFloatView.show();
                }
            } else if (type == CLOSE) {
                if (mFloatView != null && mFloatView.isShow() == true) {
                    mFloatView.close();
                }
                stopSelf();
            }
        }
 
        return super.onStartCommand(intent, flags, startId);
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRefresh);
    }
 
}

————————————————

原文链接:https://blog.csdn.net/aqi00/article/details/52173979/

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 Android 的自定义悬浮页面磁吸四周工具类的示例代码。 首先,我们需要定义一个 FloatingView 类,它继承自 FrameLayout,并实现了 View.OnTouchListener 接口。这个类表示悬浮页面,可以包含任意的子 View。 ``` public class FloatingView extends FrameLayout implements View.OnTouchListener { private int mLastX; private int mLastY; private int mStartX; private int mStartY; private int mScreenWidth; private int mScreenHeight; public FloatingView(Context context) { this(context, null); } public FloatingView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FloatingView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setOnTouchListener(this); mScreenWidth = getResources().getDisplayMetrics().widthPixels; mScreenHeight = getResources().getDisplayMetrics().heightPixels; } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = (int) event.getRawX(); mStartY = (int) event.getRawY(); mLastX = mStartX; mLastY = mStartY; break; case MotionEvent.ACTION_MOVE: int dx = (int) (event.getRawX() - mLastX); int dy = (int) (event.getRawY() - mLastY); int x = getX() + dx; int y = getY() + dy; if (x < 0) { x = 0; } if (x > mScreenWidth - getWidth()) { x = mScreenWidth - getWidth(); } if (y < 0) { y = 0; } if (y > mScreenHeight - getHeight()) { y = mScreenHeight - getHeight(); } setX(x); setY(y); mLastX = (int) event.getRawX(); mLastY = (int) event.getRawY(); break; case MotionEvent.ACTION_UP: if (Math.abs(event.getRawX() - mStartX) < 5 && Math.abs(event.getRawY() - mStartY) < 5) { performClick(); } break; } return true; } } ``` 接下来,我们编写一个辅助类,用于将悬浮页面磁吸到屏幕的四周。该类需要维护一个 FloatingView 对象,以及一些悬浮的位置信息。 ``` public class FloatingViewHelper { private static final int MARGIN = 10; private FloatingView mFloatingView; private int mScreenWidth; private int mScreenHeight; private int mLeft; private int mTop; private int mRight; private int mBottom; public FloatingViewHelper(Context context, FloatingView floatingView) { mFloatingView = floatingView; mScreenWidth = context.getResources().getDisplayMetrics().widthPixels; mScreenHeight = context.getResources().getDisplayMetrics().heightPixels; } public void attachToWindow() { WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; params.format = PixelFormat.RGBA_8888; params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.gravity = Gravity.TOP | Gravity.LEFT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.height = WindowManager.LayoutParams.WRAP_CONTENT; mFloatingView.setLayoutParams(params); mFloatingView.setOnTouchListener(mFloatingView); WindowManager wm = (WindowManager) mFloatingView.getContext().getSystemService(Context.WINDOW_SERVICE); wm.addView(mFloatingView, params); } public void detachFromWindow() { WindowManager wm = (WindowManager) mFloatingView.getContext().getSystemService(Context.WINDOW_SERVICE); wm.removeView(mFloatingView); } public void snapToEdge() { int centerX = (mLeft + mRight) / 2; int centerY = (mTop + mBottom) / 2; if (centerX < mScreenWidth / 2) { mFloatingView.setX(0 + MARGIN); mLeft = 0; mRight = mLeft + mFloatingView.getWidth(); } else { mFloatingView.setX(mScreenWidth - mFloatingView.getWidth() - MARGIN); mRight = mScreenWidth; mLeft = mRight - mFloatingView.getWidth(); } if (centerY < mScreenHeight / 2) { mFloatingView.setY(0 + MARGIN); mTop = 0; mBottom = mTop + mFloatingView.getHeight(); } else { mFloatingView.setY(mScreenHeight - mFloatingView.getHeight() - MARGIN); mBottom = mScreenHeight; mTop = mBottom - mFloatingView.getHeight(); } } public void updatePosition() { mLeft = (int) mFloatingView.getX(); mTop = (int) mFloatingView.getY(); mRight = mLeft + mFloatingView.getWidth(); mBottom = mTop + mFloatingView.getHeight(); } } ``` 最后,我们可以在 Activity 中使用这个辅助类来实现悬浮的磁吸效果。 ``` public class MainActivity extends AppCompatActivity { private FloatingView mFloatingView; private FloatingViewHelper mFloatingViewHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mFloatingView = new FloatingView(this); mFloatingView.setBackgroundColor(Color.YELLOW); mFloatingViewHelper = new FloatingViewHelper(this, mFloatingView); mFloatingViewHelper.attachToWindow(); } @Override protected void onDestroy() { super.onDestroy(); mFloatingViewHelper.detachFromWindow(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { mFloatingViewHelper.snapToEdge(); mFloatingViewHelper.updatePosition(); } } } ``` 这里,我们在 onCreate 方法中创建了一个 FloatingView 对象,并将其添加到 Activity 的界面中。然后,我们创建了一个 FloatingViewHelper 对象,将其附加到 FloatingView 上,并将其添加到 WindowManager 中显示。在 onDestroy 方法中,我们从 WindowManager 中移除了 FloatingView。 在 onWindowFocusChanged 方法中,我们在 Activity 获取焦点时调用了 snapToEdge 方法,将 FloatingView 磁吸到屏幕的四周。我们还调用了 updatePosition 方法,更新了悬浮的位置信息。这样,我们就实现了一个简单的悬浮页面磁吸四周工具类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值