Android 悬浮窗实现demo

举报
xq9527 发表于 2022/06/09 20:31:48 2022/06/09
【摘要】 前言:最近在公司开发手游SDK的时候,需要做一个浮窗功能 虽然网上有各种现成的案例 但是我这边发现很多问题 所以就重新写了一个 不过月还是基于 WindowManager 实现的 效果图: 可以拖拽的悬浮窗 : 初始化 WindowManager 和 WindowManager.LayoutParams private void initView(){ if(existVi...

前言:

最近在公司开发手游SDK的时候,需要做一个浮窗功能 虽然网上有各种现成的案例 但是我这边发现很多问题 所以就重新写了一个 不过月还是基于 WindowManager 实现的

效果图:

6865547-dc961481198387e9.png
6865547-c2e9cda7788239fb.gif

可以拖拽的悬浮窗 :

  • 初始化 WindowManager 和 WindowManager.LayoutParams

	private void initView(){
		if(existView){
			return;
		}
		mScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
		mScreenHeigh=mContext.getResources().getDisplayMetrics().heightPixels;
		mManager = (WindowManager) mContext
				.getSystemService(Context.WINDOW_SERVICE);
		params = new WindowManager.LayoutParams(
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.LAST_APPLICATION_WINDOW,
				WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
						| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
						| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,// FLAG_LAYOUT_NO_LIMITS使x轴可以超出手机屏幕外
				PixelFormat.TRANSLUCENT);
		params.gravity= Gravity.TOP | Gravity.LEFT | Gravity.CENTER_VERTICAL;
		params.width = WindowManager.LayoutParams.WRAP_CONTENT;
		//需要根据横竖屏处理
		params.x=0;
//		params.y=mScreenHeigh/2-100;
		params.y = 100;
		findViewByid(1);
		mHandler.postDelayed(run, 2000);		
	}

  • 加载布局

@SuppressLint("Range")
	private void findViewByid(int type){
		if(floatView!=null){
			mManager.removeView(floatView);
		}
		if(type==1){
			floatView= LayoutInflater.from(mContext).inflate(ResourceUtil.getLayoutId(mContext, KR.layout.cs_floatview_left), null, false);
			leftLayout=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_floatview_left_extends));
			ll_csfloat_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_hint));
			ll_csfloat_right_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_right_hint));
		}else{
			floatView= LayoutInflater.from(mContext).inflate(ResourceUtil.getLayoutId(mContext, KR.layout.cs_floatview_right), null, false);
			leftLayout=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_floatview_right_extends));
			ll_csfloat_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_right_hint));
		}
		
		floatView.setAlpha(100);
		imageview=(ImageView)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.iv_floatview_left));
		leftLayout.setVisibility(View.GONE);
		ll_csfloat_hint.setVisibility(View.GONE);
		imageview.setOnTouchListener(new ImageviewOnTouch());
		imageview.getViewTreeObserver().addOnGlobalLayoutListener(
				new OnGlobalLayoutListener() {
					@Override
					public void onGlobalLayout() {
						mViewWidth = imageview.getWidth();
						mViewheight=imageview.getHeight();
						imageview.getViewTreeObserver()
								.removeGlobalOnLayoutListener(this);
					}
				});
		mManager.addView(floatView, params);
		setOnClickListener();
		existView=true;
		if(isRegister){
			setTimer();
		}
	}

  • 完整代码

package com.example.myapplication.drag;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.myapplication.KR;
import com.example.myapplication.NineFunSdkGiftbagActivity;
import com.example.myapplication.ResourceUtil;
import java.util.Timer;
import java.util.TimerTask;

/***
 *
 *类说明 :悬浮窗小球
 *
 *
 */

public class CSFloatView {
	
	private Context mContext;
	private View floatView;
	private WindowManager mManager;
	private WindowManager.LayoutParams params;
	private LinearLayout leftLayout,ll_csfloat_hint,ll_csfloat_right_hint;
	private ImageView imageview;
	private int mScreenWidth,mScreenHeigh;
	private boolean isDowned=false;
	private int mViewWidth,mViewheight;
	private static boolean existView=false;
	private TextView mTxtAccount , mTxtService , mTxtGame , mTxtHide ,mfloatHide;
	public static final int ACCOUNT_MANAGER = 0 ;
	public static final int GAME_SERVICE = 1 ;
	public static final int MORE_GAME = 2 ;
	public static final int HIDE_ICON = 3 ;
	public static final int FLOAT_MENU_LAYOUT = 4 ;
	private boolean isRegister=false;
	private boolean timerIsRun=false;
	private Timer timer;
	private boolean isLeft=true;


	
	public CSFloatView(Context context, boolean register) {
		mContext=context;
		isRegister=register;
		initView();
	}

	public void setIsRegister(boolean register){
		isRegister=register;
		if(isRegister){
			setTimer();
		}
	}

	private void initView(){
		if(existView){
			return;
		}
		mScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
		mScreenHeigh=mContext.getResources().getDisplayMetrics().heightPixels;
		mManager = (WindowManager) mContext
				.getSystemService(Context.WINDOW_SERVICE);
		params = new WindowManager.LayoutParams(
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.WRAP_CONTENT,
				WindowManager.LayoutParams.LAST_APPLICATION_WINDOW,
				WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
						| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
						| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,// FLAG_LAYOUT_NO_LIMITS使x轴可以超出手机屏幕外
				PixelFormat.TRANSLUCENT);
		
		params.gravity= Gravity.TOP | Gravity.LEFT | Gravity.CENTER_VERTICAL;
		params.width = WindowManager.LayoutParams.WRAP_CONTENT;
		//需要根据横竖屏处理
		params.x=0;
//		params.y=mScreenHeigh/2-100;
		params.y = 100;
		findViewByid(1);
		mHandler.postDelayed(run, 2000);		
	}
	
	
	private int initialX;
	private int initialY;
	private float initialTouchX;
	private float initialTouchY;
	private int delay;
	class ImageviewOnTouch implements OnTouchListener {

		@Override
		public boolean onTouch(View view, MotionEvent event) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				initialX = params.x;
				initialY = params.y;
				initialTouchX = event.getRawX();
				initialTouchY = event.getRawY();
				mHandler.removeCallbacks(run);
				break;

			case MotionEvent.ACTION_UP:
				
				if(!isDowned){
					
					if(Math.abs((event.getRawX() - initialTouchX))>10){
						
						Log.e("tag", "移动:"+(event.getRawX() - initialTouchX));
						if(initialX < 0){
							params.x = 0 ;
							mManager.updateViewLayout(floatView, params);
						}
						if(initialX > mScreenWidth - mViewWidth ){
							params.x = mScreenWidth - mViewWidth;
							mManager.updateViewLayout(floatView, params);
						}
						if(params.y<0){
							params.y=0;
						}if(params.y+mViewheight>=mScreenHeigh){
							params.y=mScreenHeigh-mViewheight;
						}
						mHandler.postDelayed(run, 2000);
						
					}else{

						params.width=mViewWidth*5;
						if(params.x>mScreenWidth/2){
							isLeft=false;
							findViewByid(0);
						}else{
							isLeft=true;
							if(params.x<0){
								params.x=0;
							}
							findViewByid(1);
						}
						delay=params.x;
						isDowned = false;
						//弹出菜单界面
						mContext.startActivity(new Intent(mContext, NineFunSdkGiftbagActivity.class));
						mManager.updateViewLayout(floatView, params);
						mHandler.postDelayed(run, 2000);

					}

				}else{
					mHandler.removeCallbacks(run);
					isDowned=false;
					if(!isLeft){
						leftLayout.setVisibility(View.GONE);
					}else{
						leftLayout.setVisibility(View.GONE);
						params.width=mViewWidth;
					}
					if(delay>=(mScreenWidth-mViewWidth/2)){
						params.x=mScreenWidth-mViewWidth;
					}else{
						params.x=delay;
					}

					mManager.updateViewLayout(floatView, params);
					mHandler.postDelayed(run, 2000);
				}
				

				break;
			case MotionEvent.ACTION_MOVE:
				if(!isDowned){
					params.x = initialX + (int) (event.getRawX() - initialTouchX);
					params.y = initialY + (int) (event.getRawY() - initialTouchY);
					mManager.updateViewLayout(floatView, params);
				}
				break;
			}
			return true;
		}
		
	}

	private void updateView(){

		if(params.x>(mScreenWidth/2)){
			params.x=(mScreenWidth-mViewWidth/3);
			params.alpha = 0.5f;
			mManager.updateViewLayout(floatView, params);
		}else{
			params.x=(mViewWidth/3)-mViewWidth;
			params.alpha = 0.5f;
			mManager.updateViewLayout(floatView, params);
		}
		params.alpha = 1f;
	}
	
	private Handler mHandler=new Handler();
	Runnable run=new Runnable() {
		
		@Override
		public void run() {
			updateView();
		}
	};
	
	@SuppressLint("Range")
	private void findViewByid(int type){
		if(floatView!=null){
			mManager.removeView(floatView);
		}
		if(type==1){
			floatView= LayoutInflater.from(mContext).inflate(ResourceUtil.getLayoutId(mContext, KR.layout.cs_floatview_left), null, false);
			leftLayout=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_floatview_left_extends));
			ll_csfloat_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_hint));
			ll_csfloat_right_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_right_hint));
		}else{
			floatView= LayoutInflater.from(mContext).inflate(ResourceUtil.getLayoutId(mContext, KR.layout.cs_floatview_right), null, false);
			leftLayout=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_floatview_right_extends));
			ll_csfloat_hint=(LinearLayout)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.ll_csfloat_right_hint));
		}
		
		floatView.setAlpha(100);
		imageview=(ImageView)floatView.findViewById(ResourceUtil.getId(mContext, KR.id.iv_floatview_left));
		leftLayout.setVisibility(View.GONE);
		ll_csfloat_hint.setVisibility(View.GONE);
		imageview.setOnTouchListener(new ImageviewOnTouch());
		imageview.getViewTreeObserver().addOnGlobalLayoutListener(
				new OnGlobalLayoutListener() {
					@Override
					public void onGlobalLayout() {
						mViewWidth = imageview.getWidth();
						mViewheight=imageview.getHeight();
						imageview.getViewTreeObserver()
								.removeGlobalOnLayoutListener(this);
					}
				});
		mManager.addView(floatView, params);
		setOnClickListener();
		existView=true;
		if(isRegister){
			setTimer();
		}
	}
	
	private void setOnClickListener(){
		mTxtAccount  = (TextView) floatView.findViewById(ResourceUtil.getId(mContext, KR.id.txt_floatmenu_account));
		mTxtService  = (TextView) floatView.findViewById(ResourceUtil.getId(mContext, KR.id.txt_floatmenu_service));
		mTxtGame  = (TextView) floatView.findViewById(ResourceUtil.getId(mContext, KR.id.txt_floatmenu_game));
		mTxtHide  = (TextView) floatView.findViewById(ResourceUtil.getId(mContext, KR.id.txt_floatmenu_hide));
		TextViewOnClick ciickLitener=new TextViewOnClick();
		mTxtAccount.setOnClickListener(ciickLitener);
		mTxtGame.setOnClickListener(ciickLitener);
		mTxtHide.setOnClickListener(ciickLitener);
		mTxtService.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				Toast.makeText(mContext,"客服", Toast.LENGTH_SHORT).show()
			}
		});
		
	}
	
	class TextViewOnClick implements OnClickListener {

		@Override
		public void onClick(View v) {

			Toast.makeText(mContext,""+v.getId(), Toast.LENGTH_SHORT).show();


		}
		
	}
	
	public void hideFloatView(){
		if(mManager!=null&&floatView!=null){
			mHandler.removeCallbacks(run);
//			mManager.removeView(floatView);
			mManager.removeViewImmediate(floatView);
			mManager=null;
			existView=false;
		}

	}

	private void setTimer(){
		if(timerhandler!=null){
			timerhandler.removeMessages(1);
			timerhandler.removeMessages(2);
		}if(timer==null){
			timer=new Timer();
		}else{
			timer.cancel();
			timer=new Timer();
		}
		timer.schedule(new TimerTask() {

			@Override
			public void run() {
				timerhandler.sendEmptyMessage(1);
			}
		}, 5000);
	}

	private Handler timerhandler=new Handler(){
		public void handleMessage(Message msg) {
			if(msg.what==1){

				if(leftLayout!=null&&leftLayout.getVisibility()== View.GONE&&ll_csfloat_hint!=null&&ll_csfloat_hint.getVisibility()== View.GONE){

					ll_csfloat_hint.setVisibility(View.VISIBLE);
					timerhandler.sendEmptyMessageDelayed(2, 3000);
					isRegister=false;
				}

			}else if(msg.what==2){
				ll_csfloat_hint.setVisibility(View.GONE);

			}

		};
	};
}

悬浮球布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <LinearLayout
        android:id="@+id/ll_csfloat_right_hint"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:paddingRight="10dp"
            android:text="如有问题,在此点客服咨询"
            android:textColor="#FFFFFF"
            android:textSize="13sp" />

    </LinearLayout>

    <ImageView
        android:id="@+id/iv_floatview_left"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:background="@drawable/cs_floatmenu_main" />

    <LinearLayout
        android:id="@+id/ll_csfloat_hint"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        >

        <!--tools:visibility="visible"-->

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:paddingLeft="18dp"
            android:text="如有问题,在此点客服咨询"
            android:textColor="#FFFFFF"
            android:textSize="14sp" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_floatview_left_extends"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_weight="1"

        android:gravity="center_vertical"
        android:visibility="gone"
        >

        <!--tools:visibility="visible"-->

        <TextView
            android:id="@+id/txt_floatmenu_account"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:text="账号"
            android:textAppearance="?android:attr/textAppearanceSmall"

            android:textColor="#FFFFFF" />

        <TextView
            android:id="@+id/txt_floatmenu_service"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:text="客服"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="#FFFFFF" />

        <TextView
            android:id="@+id/txt_floatmenu_game"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"

            android:gravity="center"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:text="游戏群"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="#FFFFFF" />

        <TextView
            android:id="@+id/txt_floatmenu_hide"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"

            android:gravity="center"
            android:paddingBottom="4dp"
            android:paddingTop="4dp"
            android:text="隐藏"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="#FFFFFF" />
    </LinearLayout>
</LinearLayout>

布局预览

6865547-0f30f773ad825d8c.png

具体调用

  • 显示浮窗

        findViewById(R.id.show).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                FloatMenuGiftbag floatMenuGiftbag=FloatMenuGiftbag.getInstance();
                floatMenuGiftbag.showFloatMenu(context);
                FloatMenuManager.getInstance().showFloatMenu(context);

            }
        });
  • 隐藏浮窗

findViewById(R.id.hide).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                FloatMenuGiftbag floatMenuGiftbag=FloatMenuGiftbag.getInstance();
                floatMenuGiftbag.hideFloatMenu();
                FloatMenuManager.getInstance().hideFloatMenu();

            }
        });

到此我们的悬浮窗效果 就讲完了

最后总结:

整个悬浮窗的效果我们 这边就讲的差不多了 主要是要用官方的 WindowManager 来实现的。我们要注意我们在退出activity要处理好退出逻辑否则会出现下次再次显示崩溃问题 unable to add window 所以在对接游戏的我们注意 这点让游戏直接调用我们退出逻辑

项目地址:

码云 :https://gitee.com/qiuyu123/floatdemo.git

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。