Android仿騰訊視頻實現懸浮窗效果
前言
相信大傢對Android懸浮窗應該是很熟悉瞭,比如說騰訊視頻、愛奇藝等APP都有懸浮窗功能。在你打遊戲的同時還可以看視頻,充分利用屏幕空間。還有微信,360手機衛士等APP也有懸浮窗功能。那麼Android懸浮窗是怎麼實現的呢?
項目源碼:Android仿騰訊視頻懸浮窗的實現
其實並不難,核心代碼就隻有一行:
windowManager.addView(view, layoutParams)
效果圖
對view比較熟悉的同學們應該發現瞭,其實我們的懸浮窗就是一個view,我把隻需要把view添加到windowManager上就可以瞭。那麼,開始講細節瞭:
權限一定要記得加:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
因為我們的懸浮窗要在Launcher上或者在其他APP上面運行,所以這裡就用到瞭service,因為service可以默默地在後臺運行。
實現大致步驟:
1.檢查權限(如果沒有權限跳轉到授權界面)
2.在service中用inflate方法獲取我們需要的view,設置位置參數等,加入到windowManager裡面
3.啟動懸浮窗服務
view佈局
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_width="200dp" android:layout_height="100dp" android:src="@drawable/huge"></ImageView> <ImageView android:id="@+id/close" android:src="@drawable/close" android:layout_width="30dp" android:layout_height="30dp" android:layout_marginLeft="170dp"> </ImageView> </FrameLayout>
對應的界面:
FloatingWindowService
package com.example.floatingwindow import android.annotation.SuppressLint import android.app.Service import android.content.Context import android.content.Intent import android.os.Build import android.os.IBinder import android.view.* import android.widget.ImageView import android.widget.Toast class FloatingWindowService : Service(){ private lateinit var layoutParams: WindowManager.LayoutParams private lateinit var windowManager: WindowManager override fun onBind(intent: Intent): IBinder? { // TODO: Return the communication channel to the service. throw UnsupportedOperationException("Not yet implemented") } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { showFloatingWindow() return super.onStartCommand(intent, flags, startId) } @SuppressLint("ClickableViewAccessibility") private fun showFloatingWindow() { // 獲取WindowManager服務 windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager // 新建懸浮窗控件 val view = LayoutInflater.from(this).inflate(R.layout.window, null) // 設置LayoutParam layoutParams = WindowManager.LayoutParams() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE } //設置位置 layoutParams.gravity = Gravity.LEFT or Gravity.TOP layoutParams.x = windowManager.defaultDisplay.width layoutParams.y = 200 //設置flag layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH //設置view的寬高 layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT //添加拖拽事件 view.setOnTouchListener(FloatingOnTouchListener()) val close = view.findViewById<ImageView>(R.id.close) close.setOnClickListener { stopSelf() windowManager.removeView(view) Toast.makeText(this,"close",Toast.LENGTH_SHORT).show() } // 將懸浮窗控件添加到WindowManager windowManager.addView(view, layoutParams) } override fun onDestroy() { super.onDestroy() Toast.makeText(this,"onDestroy",Toast.LENGTH_SHORT).show() } inner class FloatingOnTouchListener : View.OnTouchListener { private var x = 0f private var y = 0f @SuppressLint("ClickableViewAccessibility") override fun onTouch(v: View?, event: MotionEvent?): Boolean { when(event?.action){ MotionEvent.ACTION_DOWN ->{ x = event.rawX y = event.rawY } MotionEvent.ACTION_MOVE ->{ val nowX = event.rawX val nowY = event.rawY val movedX = nowX - x val movedY = nowY - y x = nowX y = nowY layoutParams.x = (layoutParams.x + movedX).toInt() layoutParams.y = (layoutParams.y + movedY).toInt() windowManager.updateViewLayout(v, layoutParams) } MotionEvent.ACTION_UP ->{ } } return false } } }
先獲取windowManager,加載我們的懸浮窗view,這裡的TYPE_APPLICATION_OVERLAY的作用是把我們的view設置成系統頂層窗口,顯示在其他一切內容之上。TYPE_SYSTEM_OVERLAY的作用也是一樣的,隻不過現在被遺棄調瞭。
設置初始位置:
初始位置,這裡可以看一下Android坐標系相關知識,Android 零坐標在屏幕左上方。這裡設置一下xy坐標的位置就可以。
設置flag:
設置flag的作用是讓view不獲取焦點。如果不做處理,view會遮住屏幕其他控件的點擊事件。
拖拽功能:
FloatingOnTouchListener是一個內部類,它可以使用FloatingWindowService類中的變量。實現OnTouchListener接口,當屏幕點擊時記錄下當前位置,屏幕滑動時計算出劃過的距離,修改layoutParams的xy坐標,調用windowManager.updateViewLayout(v, layoutParams)方法就可以更新view當前位置。
MainActivity
package com.example.floatingwindow import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.widget.Toast import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) startFloatingService() } private fun startFloatingService() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { startActivityForResult(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")), 0) } else { startService(Intent(this@MainActivity, FloatingWindowService::class.java)) } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 0) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "授權失敗", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "授權成功", Toast.LENGTH_SHORT).show() startService(Intent(this@MainActivity, FloatingWindowService::class.java)) } } } } }
到這裡懸浮窗的實現基本就結束瞭。
碼雲項目源碼:Android仿騰訊視頻懸浮窗的實現
以上就是Android仿騰訊視頻實現懸浮窗效果的詳細內容,更多關於android懸浮窗的資料請關註WalkonNet其它相關文章!