Android 悬浮窗实现demo
2021-10-19 本文已影响0人
xq9527
前言:
最近在公司开发手游SDK的时候,需要做一个浮窗功能 虽然网上有各种现成的案例 但是我这边发现很多问题 所以就重新写了一个 不过月还是基于 WindowManager 实现的
效果图:
image.png d1203a5db97a5a5cc2db14a12ccbb801[00_00_03--00_00_23].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>
布局预览
image.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 所以在对接游戏的我们注意 这点让游戏直接调用我们退出逻辑