可拖拽懸浮窗、對話框懸浮窗的簡單實現

  

 

  本文講解的是Android的懸浮窗機制,這個懸浮窗在不少第三方ROM會被屏蔽,像是小米,錘子上都沒法顯示。小米卻是能夠經過開關開啓,但在錘子上根本連開的機會都沒有,真是無奈啊…… 雖然懸浮窗在實際中比較難以推廣,但學習方面仍是沒問題的啦。android

 

1、常規懸浮窗windows

思路:app

1.創建一個服務,而且在裏面生成一個WindowManager對象,經過它來加載一個視圖做爲懸浮窗。框架

2.設置WindowManager的參數Paramside

3.設置一個容器來找到懸浮窗的父控件,並綁定到windowManager中去佈局

4.經過父控件來加載懸浮窗的視圖學習

 

實現:this

佈局文件:spa

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

    <ImageButton
        android:id="@+id/floating_imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

 

JAVA代碼:.net

    /**
     * 定義浮動窗口布局
     */
    LinearLayout mlayout;
    /**
     * 懸浮窗控件
     */
    ImageView mfloatingIv;
    /**
     * 懸浮窗的佈局
     */
    WindowManager.LayoutParams wmParams;
    LayoutInflater inflater;
    /**
     * 建立浮動窗口設置佈局參數的對象
     */
    WindowManager mWindowManager;

 

1.初始化windowManager,而且找到控件

/**
     * 初始化windowManager
     */
    private void initWindow() {
        mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
        wmParams = getParams(wmParams);//設置好懸浮窗的參數
        // 懸浮窗默認顯示以左上角爲起始座標
        wmParams.gravity = Gravity.LEFT| Gravity.TOP;
        //懸浮窗的開始位置,由於設置的是從左上角開始,因此屏幕左上角是x=0;y=0        
        wmParams.x = 50;
        wmParams.y = 50;
        //獲得容器,經過這個inflater來得到懸浮窗控件
        inflater = LayoutInflater.from(getApplication()); // 獲取浮動窗口視圖所在佈局
        mlayout = (LinearLayout) inflater.inflate(R.layout.floating_layout, null); // 添加懸浮窗的視圖
        mWindowManager.addView(mlayout, wmParams);
    }
    
    /** 對windowManager進行設置
     * @param wmParams
     * @return
     */
    public WindowManager.LayoutParams getParams(WindowManager.LayoutParams wmParams){
        wmParams = new WindowManager.LayoutParams();
        //設置window type 下面變量2002是在屏幕區域顯示,2003則能夠顯示在狀態欄之上
        //wmParams.type = LayoutParams.TYPE_PHONE; 
        //wmParams.type = LayoutParams.TYPE_SYSTEM_ALERT; 
        wmParams.type = LayoutParams.TYPE_SYSTEM_ERROR; 
        //設置圖片格式,效果爲背景透明
        wmParams.format = PixelFormat.RGBA_8888; 
        //設置浮動窗口不可聚焦(實現操做除浮動窗口外的其餘可見窗口的操做)
       //wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE; 
        //設置能夠顯示在狀態欄上
        wmParams.flags =  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR|
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        
        //設置懸浮窗口長寬數據  
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        return wmParams;
    }

 

2.在懸浮窗控件中設置事件

/**
     * 找到懸浮窗的圖標,而且設置事件
     * 設置懸浮窗的點擊、滑動事件
     */
    private void initFloating() {
        mfloatingIv = (ImageButton) mlayout.findViewById(R.id.floating_imageView);
        mfloatingIv.getBackground().setAlpha(150);

        mGestureDetector = new GestureDetector(this, new MyOnGestureListener()); //設置監聽器
        mfloatingIv.setOnTouchListener(new FloatingListener());
    }
    

 

3.設置懸浮窗的拖拽事件和點擊事件

    //觸摸監聽器
    GestureDetector mGestureDetector;

 

   //開始觸控的座標,移動時的座標(相對於屏幕左上角的座標)
    private int mTouchStartX,mTouchStartY,mTouchCurrentX,mTouchCurrentY;
    //開始時的座標和結束時的座標(相對於自身控件的座標)
    private int mStartX,mStartY,mStopX,mStopY;
    private boolean isMove;//判斷懸浮窗是否移動
    
    /**
     * @author:金凱
     * @tips  :本身寫的懸浮窗監聽器
     * @date  :2014-3-28
     */
    private class FloatingListener implements OnTouchListener{

        @Override
        public boolean onTouch(View arg0, MotionEvent event) {

            int action = event.getAction();
            switch(action){ 
                case MotionEvent.ACTION_DOWN:
                    isMove = false;
                    mTouchStartX = (int)event.getRawX();
                    mTouchStartY = (int)event.getRawY();
                    mStartX = (int)event.getX();
                    mStartY = (int)event.getY();
                    break; 
                case MotionEvent.ACTION_MOVE:  
                    mTouchCurrentX = (int) event.getRawX();
                    mTouchCurrentY = (int) event.getRawY();
                    wmParams.x += mTouchCurrentX - mTouchStartX;
                    wmParams.y += mTouchCurrentY - mTouchStartY;
                    mWindowManager.updateViewLayout(mlayout, wmParams);
                    
                    mTouchStartX = mTouchCurrentX;
                    mTouchStartY = mTouchCurrentY; 
                    break;
                case MotionEvent.ACTION_UP:
                    mStopX = (int)event.getX();
                    mStopY = (int)event.getY();
                    //System.out.println("|X| = "+ Math.abs(mStartX - mStopX));
                    //System.out.println("|Y| = "+ Math.abs(mStartY - mStopY));
                    if(Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1){
                        isMove = true;
                    }
                    break; 
            }
            return mGestureDetector.onTouchEvent(event);  //此處必須返回false,不然OnClickListener獲取不到監聽
        }

    }
    
    /**
     * @author:金凱
     * @tips  :本身定義的手勢監聽類
     * @date  :2014-3-29
     */
    class MyOnGestureListener extends SimpleOnGestureListener {
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (!isMove) {
                Toast.makeText(getApplicationContext(), "你點擊了懸浮窗", 0).show();
                System.out.println("onclick");
            }
            return super.onSingleTapConfirmed(e);
        }
    }

 

4.服務的框架

   @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        initWindow();//設置窗口的參數
    }
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        
        initFloating();//設置懸浮窗圖標
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mlayout != null) {    
            // 移除懸浮窗口
            mWindowManager.removeView(mlayout);
        }
    }
    

 

所有代碼:

package com.kale.testfloating;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

/**
 * @author:Jack Tony
 * 
 * 重要:注意要申請權限!!!!
 *  <!-- 懸浮窗的權限 -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 * 
 * @tips  :思路:
 * 1.得到一個windowManager類
 * 2.經過wmParams設置好windows的各類參數
 * 3.得到一個視圖的容器,找到懸浮窗視圖的父控件,好比linearLayout
 * 4.將父控件添加到WindowManager中去
 * 5.經過這個父控件找到要顯示的懸浮窗圖標,並進行拖動或點擊事件的設置
 * @date  :2014-9-25
 */
public class FloatingService extends Service{
    /**
     * 定義浮動窗口布局
     */
    LinearLayout mlayout;
    /**
     * 懸浮窗控件
     */
    ImageView mfloatingIv;
    /**
     * 懸浮窗的佈局
     */
    WindowManager.LayoutParams wmParams;
    LayoutInflater inflater;
    /**
     * 建立浮動窗口設置佈局參數的對象
     */
    WindowManager mWindowManager;

    //觸摸監聽器
    GestureDetector mGestureDetector;
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        initWindow();//設置窗口的參數
    }
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        
        initFloating();//設置懸浮窗圖標
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mlayout != null) {    
            // 移除懸浮窗口
            mWindowManager.removeView(mlayout);
        }
    }
    
    ///////////////////////////////////////////////////////////////////////
    
    /**
     * 初始化windowManager
     */
    private void initWindow() {
        mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
        wmParams = getParams(wmParams);//設置好懸浮窗的參數
        // 懸浮窗默認顯示以左上角爲起始座標
        wmParams.gravity = Gravity.LEFT| Gravity.TOP;
        //懸浮窗的開始位置,由於設置的是從左上角開始,因此屏幕左上角是x=0;y=0        
        wmParams.x = 50;
        wmParams.y = 50;
        //獲得容器,經過這個inflater來得到懸浮窗控件
        inflater = LayoutInflater.from(getApplication());
        // 獲取浮動窗口視圖所在佈局
        mlayout = (LinearLayout) inflater.inflate(R.layout.floating_layout, null);
        // 添加懸浮窗的視圖
        mWindowManager.addView(mlayout, wmParams);
    }
    
    /** 對windowManager進行設置
     * @param wmParams
     * @return
     */
    public WindowManager.LayoutParams getParams(WindowManager.LayoutParams wmParams){
        wmParams = new WindowManager.LayoutParams();
        //設置window type 下面變量2002是在屏幕區域顯示,2003則能夠顯示在狀態欄之上
        //wmParams.type = LayoutParams.TYPE_PHONE; 
        //wmParams.type = LayoutParams.TYPE_SYSTEM_ALERT; 
        wmParams.type = LayoutParams.TYPE_SYSTEM_ERROR; 
        //設置圖片格式,效果爲背景透明
        wmParams.format = PixelFormat.RGBA_8888; 
        //設置浮動窗口不可聚焦(實現操做除浮動窗口外的其餘可見窗口的操做)
       //wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE; 
        //設置能夠顯示在狀態欄上
        wmParams.flags =  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR|
        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        
        //設置懸浮窗口長寬數據  
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        return wmParams;
    }
    
    /**
     * 找到懸浮窗的圖標,而且設置事件
     * 設置懸浮窗的點擊、滑動事件
     */
    private void initFloating() {
        mfloatingIv = (ImageButton) mlayout.findViewById(R.id.floating_imageView);
        mfloatingIv.getBackground().setAlpha(150);

        mGestureDetector = new GestureDetector(this, new MyOnGestureListener());
        //設置監聽器
        mfloatingIv.setOnTouchListener(new FloatingListener());
    }
    
    //開始觸控的座標,移動時的座標(相對於屏幕左上角的座標)
    private int mTouchStartX,mTouchStartY,mTouchCurrentX,mTouchCurrentY;
    //開始時的座標和結束時的座標(相對於自身控件的座標)
    private int mStartX,mStartY,mStopX,mStopY;
    private boolean isMove;//判斷懸浮窗是否移動
    
    /**
     * @author:金凱
     * @tips  :本身寫的懸浮窗監聽器
     * @date  :2014-3-28
     */
    private class FloatingListener implements OnTouchListener{

        @Override
        public boolean onTouch(View arg0, MotionEvent event) {

            int action = event.getAction();
            switch(action){ 
                case MotionEvent.ACTION_DOWN:
                    isMove = false;
                    mTouchStartX = (int)event.getRawX();
                    mTouchStartY = (int)event.getRawY();
                    mStartX = (int)event.getX();
                    mStartY = (int)event.getY();
                    break; 
                case MotionEvent.ACTION_MOVE:  
                    mTouchCurrentX = (int) event.getRawX();
                    mTouchCurrentY = (int) event.getRawY();
                    wmParams.x += mTouchCurrentX - mTouchStartX;
                    wmParams.y += mTouchCurrentY - mTouchStartY;
                    mWindowManager.updateViewLayout(mlayout, wmParams);
                    
                    mTouchStartX = mTouchCurrentX;
                    mTouchStartY = mTouchCurrentY; 
                    break;
                case MotionEvent.ACTION_UP:
                    mStopX = (int)event.getX();
                    mStopY = (int)event.getY();
                    //System.out.println("|X| = "+ Math.abs(mStartX - mStopX));
                    //System.out.println("|Y| = "+ Math.abs(mStartY - mStopY));
                    if(Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1){
                        isMove = true;
                    }
                    break; 
            }
            return mGestureDetector.onTouchEvent(event);  //此處必須返回false,不然OnClickListener獲取不到監聽
        }

    }
    
    /**
     * @author:金凱
     * @tips  :本身定義的手勢監聽類
     * @date  :2014-3-29
     */
    class MyOnGestureListener extends SimpleOnGestureListener {
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (!isMove) {
                Toast.makeText(getApplicationContext(), "你點擊了懸浮窗", 0).show();
                System.out.println("onclick");
            }
            return super.onSingleTapConfirmed(e);
        }
    }

    
    
}

 

記得加權限和註冊服務:

    <!-- 懸浮窗的權限 -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
  <service android:name="com.kale.testfloating.FloatingService"/>

 

2、對話框懸浮窗

由於對話框自己就是WindowManager造成的,參數也已經設置好了,因此通常不必再更改它的參數。

直接貼上所有代碼,你們會發現對話框的實現是相對簡單的。

佈局文件:

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:src="@drawable/ic_launcher" />

</LinearLayout>

 

Java代碼:

package com.kale.testfloating;

import android.app.Dialog;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;

public class DialogFloatingService extends Service {
    /**
     * 定義浮動窗口布局
     */
    Dialog mDialog;
    /**
     * 懸浮窗的佈局
     */
    WindowManager.LayoutParams wmParams;
    LayoutInflater inflater;
    /**
     * 建立浮動窗口設置佈局參數的對象
     */
    WindowManager mWindowManager;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO 自動生成的方法存根
        return null;
    }
    
    @Override
    public void onCreate() {
        // TODO 自動生成的方法存根
        super.onCreate();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO 自動生成的方法存根
        initWindow();
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        if(mDialog != null){
            mDialog.dismiss();
        }
    }
    
    /**
     * 初始化
     */
    private void initWindow() {
        mDialog = new Dialog(DialogFloatingService.this);
        mDialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
            
        //獲得容器,經過這個inflater來得到懸浮窗控件
        inflater = LayoutInflater.from(getApplication());
        // 獲取浮動窗口視圖所在佈局
        View view = inflater.inflate(R.layout.dialog_layout, null);
        
        ImageView iv = (ImageView)view.findViewById(R.id.imageView);
        iv.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO 自動生成的方法存根
                Toast.makeText(getApplicationContext(), "ImageView onclick", 0).show();
            }
        });
        
        // 添加懸浮窗的視圖
        mDialog.setContentView(view);
        mDialog.setTitle("對話框懸浮窗");
        
        mDialog.setCanceledOnTouchOutside(true);
        mDialog.show();
    }
    
    
    

}

 

3、使用方式

很簡單,就是開啓或者關閉服務~

package com.kale.testfloating;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void buttonListener(View v) {
        Intent intent = new Intent(MainActivity.this,FloatingService.class);
        switch (v.getId()) {
        case R.id.open_button:
            startService(intent);
            break;
        case R.id.close_button:
            stopService(intent);
            break;
        case R.id.open_dialog_button:
            startService(new Intent(MainActivity.this,DialogFloatingService.class));
            break;
        case R.id.close_dialog_button:
            stopService(new Intent(MainActivity.this,DialogFloatingService.class));
            break;
        default:
            break;
        }
    }
}

 

佈局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <Button
        android:id="@+id/open_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="137dp"
        android:onClick="buttonListener"
        android:text="開啓懸浮窗" />

    <Button
        android:id="@+id/close_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/open_button"
        android:layout_centerVertical="true"
        android:onClick="buttonListener"
        android:text="關閉懸浮窗" />

    <Button
        android:id="@+id/open_dialog_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/close_button"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="46dp"
        android:onClick="buttonListener"
        android:text="開啓對話框懸浮窗" />

    <Button
        android:id="@+id/close_dialog_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/open_dialog_button"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="24dp"
        android:text="關閉對話框懸浮窗" />

</RelativeLayout>

 

更加牛逼的設計方案:http://weibo.com/p/1001603823661684514597

 

源碼下載:http://download.csdn.net/detail/shark0017/7977309

相關文章
相關標籤/搜索