Android 懸浮窗實現(xiàn)demo

前言:

最近在公司開發(fā)手游SDK的時候昔穴,需要做一個浮窗功能 雖然網(wǎng)上有各種現(xiàn)成的案例 但是我這邊發(fā)現(xiàn)很多問題 所以就重新寫了一個 不過月還是基于 WindowManager 實現(xiàn)的

效果圖:

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;
        //需要根據(jù)橫豎屏處理
        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;
        //需要根據(jù)橫豎屏處理
        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

具體調(diào)用

  • 顯示浮窗

        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();

            }
        });

到此我們的懸浮窗效果 就講完了

最后總結(jié):

整個懸浮窗的效果我們 這邊就講的差不多了 主要是要用官方的 WindowManager 來實現(xiàn)的。我們要注意我們在退出activity要處理好退出邏輯否則會出現(xiàn)下次再次顯示崩潰問題 unable to add window 所以在對接游戲的我們注意 這點讓游戲直接調(diào)用我們退出邏輯

項目地址:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌良狈,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笨枯,死亡現(xiàn)場離奇詭異薪丁,居然都是意外死亡,警方通過查閱死者的電腦和手機馅精,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門严嗜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洲敢,你說我怎么就攤上這事漫玄。” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵睦优,是天一觀的道長渗常。 經(jīng)常有香客問我,道長汗盘,這世上最難降的妖魔是什么皱碘? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮隐孽,結(jié)果婚禮上癌椿,老公的妹妹穿的比我還像新娘。我一直安慰自己菱阵,他們只是感情好踢俄,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著送粱,像睡著了一般褪贵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抗俄,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音世舰,去河邊找鬼动雹。 笑死,一個胖子當著我的面吹牛跟压,可吹牛的內(nèi)容都是我干的胰蝠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼震蒋,長吁一口氣:“原來是場噩夢啊……” “哼茸塞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起查剖,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤钾虐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后笋庄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體效扫,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年直砂,在試婚紗的時候發(fā)現(xiàn)自己被綠了菌仁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡静暂,死狀恐怖济丘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洽蛀,我是刑警寧澤摹迷,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布弯院,位于F島的核電站,受9級特大地震影響泪掀,放射性物質(zhì)發(fā)生泄漏听绳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一异赫、第九天 我趴在偏房一處隱蔽的房頂上張望椅挣。 院中可真熱鬧,春花似錦塔拳、人聲如沸鼠证。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽量九。三九已至,卻和暖如春颂碧,著一層夾襖步出監(jiān)牢的瞬間荠列,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工载城, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肌似,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓诉瓦,卻偏偏與公主長得像川队,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子睬澡,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容