版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 戶外廣告牌投放合作期限合同
- 全方位行銷策劃合同
- 車庫租賃合同示例
- 電腦耗材采購合同
- 全面會議策劃服務(wù)協(xié)議
- 租房協(xié)議書簡單版
- 綠植租擺服務(wù)合同
- 房屋買賣合同中介模式
- 融資服務(wù)合作協(xié)議
- 工程合同的勞務(wù)分包協(xié)議
- 體檢報告樣表
- 《外科護理》-關(guān)節(jié)脫位病人護理
- 高血壓與體重管理
- 特殊教育家長會課件:支持特殊孩子的成長與發(fā)展
- 阿根廷文化習(xí)俗課件
- 小米智能家居裝修方案
- tpu涂層布加工工藝
- 四川省達州市宣漢縣2023-2024學(xué)年八年級上學(xué)期期末數(shù)學(xué)試題含答案解析
- 河道整治工程監(jiān)理大綱
- 《物聯(lián)網(wǎng)應(yīng)用技術(shù)》期末試卷及答案2套
- sapho綜合癥護理查房
評論
0/150
提交評論