【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)_第1頁
【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)_第2頁
【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)_第3頁
【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)_第4頁
【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)_第5頁
已閱讀5頁,還剩8頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

【移動應(yīng)用開發(fā)技術(shù)】Android懸浮窗如何實現(xiàn)

顯示浮窗public

interface

ViewManager{

//'向窗口添加視圖'

public

void

addView(View

view,

ViewGroup.LayoutParams

params);

//'更新窗口中視圖'

public

void

updateViewLayout(View

view,

ViewGroup.LayoutParams

params);

//'移除窗口中視圖'

public

void

removeView(View

view);

}

復(fù)制代碼//'解析布局文件為視圖'val

windowView

=

LayoutInflater.from(context).inflate(R.id.window_view,

null)//'獲取WindowManager系統(tǒng)服務(wù)'val

windowManager

=

context.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager//'構(gòu)建窗口布局參數(shù)'WindowManager.LayoutParams().apply

{

type

=

WindowManager.LayoutParams.TYPE_APPLICATION

width

=

WindowManager.LayoutParams.WRAP_CONTENT

height

=

WindowManager.LayoutParams.WRAP_CONTENT

gravity

=

Gravity.START

or

Gravity.TOP

x

=

0

y

=

0}.let

{

layoutParams->

//'將視圖添加到窗口'

windowManager.addView(windowView,

layoutParams)

}

復(fù)制代碼object

FloatWindow{

private

var

context:

Context?

=

null

//'當前窗口參數(shù)'

var

windowInfo:

WindowInfo?

=

null

//'把和Window布局有關(guān)的參數(shù)打包成一個內(nèi)部類'

class

WindowInfo(var

view:

View?)

{

var

layoutParams:

WindowManager.LayoutParams?

=

null

//'窗口寬'

var

width:

Int

=

0

//'窗口高'

var

height:

Int

=

0

//'窗口中是否有視圖'

fun

hasView()

=

view

!=

null

&&

layoutParams

!=

null

//'窗口中視圖是否有父親'

fun

hasParent()

=

hasView()

&&

view?.parent

!=

null

}

//'顯示窗口'

fun

show(

context:

Context,

windowInfo:

WindowInfo?,

x:

Int

=

windowInfo?.layoutParams?.x.value(),

y:

Int

=

windowInfo?.layoutParams?.y.value(),

)

{

if

(windowInfo

==

null)

{

return

}

if

(windowInfo.view

==

null)

{

return

}

this.windowInfo

=

windowInfo

this.context

=

context

//'創(chuàng)建窗口布局參數(shù)'

windowInfo.layoutParams

=

createLayoutParam(x,

y)

//'顯示窗口'

if

(!windowInfo.hasParent().value())

{

val

windowManager

=

this.context?.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager

windowManager.addView(windowInfo.view,

windowInfo.layoutParams)

}

}

//'創(chuàng)建窗口布局參數(shù)'

private

fun

createLayoutParam(x:

Int,

y:

Int):

WindowManager.LayoutParams

{

if

(context

==

null)

{

return

WindowManager.LayoutParams()

}

return

WindowManager.LayoutParams().apply

{

//'該類型不需要申請權(quán)限'

type

=

WindowManager.LayoutParams.TYPE_APPLICATION

format

=

PixelFormat.TRANSLUCENT

flags

=

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

or

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

gravity

=

Gravity.START

or

Gravity.TOP

width

=

windowInfo?.width.value()

height

=

windowInfo?.height.value()

this.x

=

x

this.y

=

y

}

}

//'為空Int提供默認值'

fun

Int?.value()

=

this

?:

0}

復(fù)制代碼val

windowView

=

LayoutInflater.from(context).inflate(R.id.window_view,

null)

WindowInfo(windowView).apply{

width

=

100

height

=

100

}.let{

windowInfo

->

FloatWindow.show(context,

windowInfo,

0,

0)

}

復(fù)制代碼浮窗背景色object

FloatWindow{

//當前窗口參數(shù)

var

windowInfo:

WindowInfo?

=

null

private

fun

createLayoutParam(x:

Int,

y:

Int):

WindowManager.LayoutParams

{

if

(context

==

null)

{

return

WindowManager.LayoutParams()

}

return

WindowManager.LayoutParams().apply

{

type

=

WindowManager.LayoutParams.TYPE_APPLICATION

format

=

PixelFormat.TRANSLUCENT

flags

=

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

or

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

or

//'設(shè)置浮窗背景變暗'

WindowManager.LayoutParams.FLAG_DIM_BEHIND

//'設(shè)置默認變暗程度為0,即不變暗,1表示全黑'

dimAmount

=

0f

gravity

=

Gravity.START

or

Gravity.TOP

width

=

windowInfo?.width.value()

height

=

windowInfo?.height.value()

this.x

=

x

this.y

=

y

}

}

//'供業(yè)務(wù)界面在需要的時候調(diào)整浮窗背景亮暗'

fun

setDimAmount(amount:Float){

windowInfo?.layoutParams?.let

{

it.dimAmount

=

amount

}

}

}

復(fù)制代碼設(shè)置浮窗點擊事件object

FloatWindow

:

View.OnTouchListener{

//'顯示窗口'

fun

show(

context:

Context,

windowInfo:

WindowInfo?,

x:

Int

=

windowInfo?.layoutParams?.x.value(),

y:

Int

=

windowInfo?.layoutParams?.y.value(),

)

{

if

(windowInfo

==

null)

{

return

}

if

(windowInfo.view

==

null)

{

return

}

this.windowInfo

=

windowInfo

this.context

=

context

//'為浮窗視圖設(shè)置觸摸監(jiān)聽器'

windowInfo.view?.setOnTouchListener(this)

windowInfo.layoutParams

=

createLayoutParam(x,

y)

if

(!windowInfo.hasParent().value())

{

val

windowManager

=

this.context?.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager

windowManager.addView(windowInfo.view,

windowInfo.layoutParams)

}

}

override

fun

onTouch(v:

View,

event:

MotionEvent):

Boolean

{

return

false

}

}

復(fù)制代碼public

class

GestureDetector

{

public

interface

OnGestureListener

{

//'ACTION_DOWN事件'

boolean

onDown(MotionEvent

e);

//'單擊事件'

boolean

onSingleTapUp(MotionEvent

e);

//'拖拽事件'

boolean

onScroll(MotionEvent

e1,

MotionEvent

e2,

float

distanceX,

float

distanceY);

...

}

}

復(fù)制代碼object

FloatWindow

:

View.OnTouchListener{

private

var

gestureDetector:

GestureDetector

=

GestureDetector(context,

GestureListener())

private

var

clickListener:

WindowClickListener?

=

null

private

var

lastTouchX:

Int

=

0

private

var

lastTouchY:

Int

=

0

//'為浮窗設(shè)置點擊監(jiān)聽器'

fun

setClickListener(listener:

WindowClickListener)

{

clickListener

=

listener

}

override

fun

onTouch(v:

View,

event:

MotionEvent):

Boolean

{

//'將觸摸事件傳遞給

GestureDetector

解析'

gestureDetector.onTouchEvent(event)

return

true

}

//'記憶起始觸摸點坐標'

private

fun

onActionDown(event:

MotionEvent)

{

lastTouchX

=

event.rawX.toInt()

lastTouchY

=

event.rawY.toInt()

}

private

class

GestureListener

:

GestureDetector.OnGestureListener

{

//'記憶起始觸摸點坐標'

override

fun

onDown(e:

MotionEvent):

Boolean

{

onActionDown(e)

return

false

}

override

fun

onSingleTapUp(e:

MotionEvent):

Boolean

{

//'點擊事件發(fā)生時,調(diào)用監(jiān)聽器'

return

clickListener?.onWindowClick(windowInfo)

?:

false

}

...

}

//'浮窗點擊監(jiān)聽器'

interface

WindowClickListener

{

fun

onWindowClick(windowInfo:

WindowInfo?):

Boolean

}

}

復(fù)制代碼拖拽浮窗object

FloatWindow

:

View.OnTouchListener{

private

var

gestureDetector:

GestureDetector

=

GestureDetector(context,

GestureListener())

private

var

lastTouchX:

Int

=

0

private

var

lastTouchY:

Int

=

0

override

fun

onTouch(v:

View,

event:

MotionEvent):

Boolean

{

//'將觸摸事件傳遞給GestureDetector解析'

gestureDetector.onTouchEvent(event)

return

true

}

private

class

GestureListener

:

GestureDetector.OnGestureListener

{

override

fun

onDown(e:

MotionEvent):

Boolean

{

onActionDown(e)

return

false

}

override

fun

onScroll(e1:

MotionEvent,e2:

MotionEvent,distanceX:

Float,distanceY:Float):

Boolean

{

//'響應(yīng)手指滾動事件'

onActionMove(e2)

return

true

}

}

private

fun

onActionMove(event:

MotionEvent)

{

//'獲取當前手指坐標'

val

currentX

=

event.rawX.toInt()

val

currentY

=

event.rawY.toInt()

//'獲取手指移動增量'

val

dx

=

currentX

-

lastTouchX

val

dy

=

currentY

-

lastTouchY

//'將移動增量應(yīng)用到窗口布局參數(shù)上'

windowInfo?.layoutParams!!.x

+=

dx

windowInfo?.layoutParams!!.y

+=

dy

val

windowManager

=

context?.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager

var

rightMost

=

screenWidth

-

windowInfo?.layoutParams!!.width

var

leftMost

=

0

val

topMost

=

0

val

bottomMost

=

screenHeight

-

windowInfo?.layoutParams!!.height

-

getNavigationBarHeight(context)

//'將浮窗移動區(qū)域限制在屏幕內(nèi)'

if

(windowInfo?.layoutParams!!.x

<

leftMost)

{

windowInfo?.layoutParams!!.x

=

leftMost

}

if

(windowInfo?.layoutParams!!.x

>

rightMost)

{

windowInfo?.layoutParams!!.x

=

rightMost

}

if

(windowInfo?.layoutParams!!.y

<

topMost)

{

windowInfo?.layoutParams!!.y

=

topMost

}

if

(windowInfo?.layoutParams!!.y

>

bottomMost)

{

windowInfo?.layoutParams!!.y

=

bottomMost

}

//'更新浮窗位置'

windowManager.updateViewLayout(windowInfo?.view,

windowInfo?.layoutParams)

lastTouchX

=

currentX

lastTouchY

=

currentY

}

}

復(fù)制代碼浮窗自動貼邊object

FloatWindow

:

View.OnTouchListener{

private

var

gestureDetector:

GestureDetector

=

GestureDetector(context,

GestureListener())

private

var

lastTouchX:

Int

=

0

private

var

lastTouchY:

Int

=

0

//'貼邊動畫'

private

var

weltAnimator:

ValueAnimator?

=

null

override

fun

onTouch(v:

View,

event:

MotionEvent):

Boolean

{

//'將觸摸事件傳遞給GestureDetector解析'

gestureDetector.onTouchEvent(event)

//'處理ACTION_UP事件'

val

action

=

event.action

when

(action)

{

MotionEvent.ACTION_UP

->

onActionUp(event,

screenWidth,

windowInfo?.width

?:

0)

else

->

{

}

}

return

true

}

private

fun

onActionUp(event:

MotionEvent,

screenWidth:

Int,

width:

Int)

{

if

(!windowInfo?.hasView().value())

{

return

}

//'記錄抬手橫坐標'

val

upX

=

event.rawX.toInt()

//'貼邊動畫終點橫坐標'

val

endX

=

if

(upX

>

screenWidth

/

2)

{

screenWidth

-

width

}

else

{

0

}

//'構(gòu)建貼邊動畫'

if

(weltAnimator

==

null)

{

weltAnimator

=

ValueAnimator.ofInt(windowInfo?.layoutParams!!.x,

endX).apply

{

interpolator

=

LinearInterpolator()

duration

=

300

addUpdateListener

{

animation

->

val

x

=

animation.animatedValue

as

Int

if

(windowInfo?.layoutParams

!=

null)

{

windowInfo?.layoutParams!!.x

=

x

}

val

windowManager

=

context?.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager

//'更新窗口位置'

if

(windowInfo?.hasParent().value())

{

windowManager.updateViewLayout(windowInfo?.view,

windowInfo?.layoutParams)

}

}

}

}

weltAnimator?.setIntValues(windowInfo?.layoutParams!!.x,

endX)

weltAnimator?.start()

}

//為空Boolean提供默認值

fun

Boolean?.value()

=

this

?:

false}

復(fù)制代碼管理多個浮窗object

FloatWindow

:

View.OnTouchListener

{

//'浮窗參數(shù)容器'

private

var

windowInfoMap:

HashMap<String,

WindowInfo?>

=

HashMap()

//'當前浮窗參數(shù)'

var

windowInfo:

WindowInfo?

=

null

//'顯示浮窗'

fun

show(

context:

Context,

//'浮窗標簽'

tag:

String,

//'若不提供浮窗參數(shù)則從參數(shù)容器中獲取該tag上次保存的參數(shù)'

windowInfo:

WindowInfo?

=

windowInfoMap[tag],

x:

Int

=

windowInfo?.layoutParams?.x.value(),

y:

Int

=

windowInfo?.layoutParams?.y.value()

)

{

if

(windowInfo

==

null)

{

return

}

if

(windowInfo.view

==

null)

{

return

}

//'更新當前浮窗參數(shù)'

this.windowInfo

=

windowInfo

//'將浮窗參數(shù)存入容器'

windowInfoMap[tag]

=

windowInfo

windowInfo.view?.setOnTouchListener(this)

this.context

=

context

windowInfo.layoutParams

=

createLayoutParam(x,

y)

if

(!windowInfo.hasParent().value())

{

val

windowManager

=this.context?.getSystemService(Context.WINDOW_SERVICE)

as

WindowManager

windowManager.addView(windowInfo.view,

windowInfo.layoutParams)

}

}

}

復(fù)制代碼監(jiān)聽浮窗界外點擊事件public

class

PopupWindow

{

/**

*

<p>Controls

whether

the

pop-up

will

be

informed

of

touch

events

outside

*

of

its

window.

*

*

@param

touchable

true

if

the

popup

should

receive

outside

*

touch

events,

false

otherwise

*/

public

void

setOutsideTouchable(boolean

touchable)

{

mOutsideTouchable

=

touchable;

}

}

復(fù)制代碼public

class

PopupWindow

{

private

int

computeFlags(int

curFlags)

{

curFlags

&=

~(

WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES

|

WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE

|

WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE

|

WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

|

WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS

|

WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM

|

WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);

...

//'如果界外可觸摸,則將FLAG_WATCH_OUTSIDE_TOUCH賦值給flag'

if

(mOutsideTouchable)

{

curFlags

|=

WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;

}

...

}

}

復(fù)制代碼public

class

PopupWindow

{

protected

final

WindowManager.LayoutParams

createPopupLayoutParams(IBinder

token)

{

final

WindowManager.LayoutParams

p

=

new

WindowManager.LayoutParams();

p.gravity

=

computeGravity();

//'計算窗口布局參數(shù)flag屬性并賦值'

p.flags

=

computeFlags(p.flags);

p.type

=

mWindowLayoutType;

p.token

=

token;

...

}

}

復(fù)制代碼public

class

PopupWindow

{

public

void

showAtLocation(IBinder

token,

int

gravity,

int

x,

int

y)

{

if

(isShowing()

||

mContentView

==

null)

{

return;

}

TransitionManager.endTransitions(mDecorView);

detachFromAnchor();

mIsShowing

=

true;

mIsDropdown

=

false;

mGravity

=

gravity;

//'構(gòu)建窗口布局參數(shù)'

final

WindowManager.LayoutParams

p

=

createPopupLayoutParams(token);

preparePopup(p);

p.x

=

x;

p.y

=

y;

invokePopup(p);

}

}

復(fù)制代碼public

class

PopupWindow

{

//'窗口根視圖'

private

class

PopupDecorView

extends

FrameLayout

{

//'窗口根視圖觸摸事件'

@Override

public

boolean

onTouchEvent(MotionEvent

event)

{

final

int

x

=

(int)

event.getX();

final

int

y

=

(int)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論