【移动应用开发技术】Android 中怎么实现桌面悬浮_第1页
【移动应用开发技术】Android 中怎么实现桌面悬浮_第2页
【移动应用开发技术】Android 中怎么实现桌面悬浮_第3页
【移动应用开发技术】Android 中怎么实现桌面悬浮_第4页
【移动应用开发技术】Android 中怎么实现桌面悬浮_第5页
已阅读5页,还剩6页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】Android中怎么实现桌面悬浮

Android中怎么实现桌面悬浮,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。实现原理

这种桌面悬浮窗的效果很类似与Widget,但是它比Widget要灵活的多。主要是通过WindowManager这个类来实现的,调用这个类的addView方法用于添加一个悬浮窗,updateViewLayout方法用于更新悬浮窗的参数,removeView用于移除悬浮窗。其中悬浮窗的参数有必要详细说明一下。WindowManager.LayoutParams这个类用于提供悬浮窗所需的参数,其中有几个经常会用到的变量:type值用于确定悬浮窗的类型,一般设为2002,表示在所有应用程序之上,但在状态栏之下。flags值用于确定悬浮窗的行为,比如说不可聚焦,非模态对话框等等,属性非常多,大家可以查看文档。gravity值用于确定悬浮窗的对齐方式,一般设为左上角对齐,这样当拖动悬浮窗的时候方便计算坐标。x值用于确定悬浮窗的位置,如果要横向移动悬浮窗,就需要改变这个值。y值用于确定悬浮窗的位置,如果要纵向移动悬浮窗,就需要改变这个值。width值用于指定悬浮窗的宽度。height值用于指定悬浮窗的高度。创建悬浮窗这种窗体需要向用户申请权限才可以的,因此还需要在AndroidManifest.xml中加入<uses-permission

android:name="android.permission.SYSTEM_ALERT_WINDOW"

/>

<uses-permission

android:name="android.permission.GET_TASKS"

/>codeactivity_main.xml<LinearLayout

xmlns:android="/apk/res/android"

xmlns:tools="/tools"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

tools:context=".MainActivity"

android:orientation="vertical"

>

<Button

android:id="@+id/start_float_window"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="打开悬浮"

>

</Button>

</LinearLayout>float_window_small.xml,用于做为小悬浮窗的布局<?xml

version="1.0"

encoding="UTF-8"?>

<LinearLayout

xmlns:android="/apk/res/android"

android:id="@+id/small_window_layout"

android:layout_width="60dip"

android:layout_height="25dip"

android:background="@drawable/bg_small"

>

<TextView

android:id="@+id/percent"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:gravity="center"

android:textColor="#ffffff"

/>

</LinearLayout>float_window_big.xml,用于做为大悬浮窗的布局<?xml

version="1.0"

encoding="UTF-8"?>

<LinearLayout

xmlns:android="/apk/res/android"

android:id="@+id/big_window_layout"

android:layout_width="200dip"

android:layout_height="100dip"

android:background="@drawable/bg_big"

android:orientation="vertical"

>

<Button

android:id="@+id/close"

android:layout_width="100dip"

android:layout_height="40dip"

android:layout_gravity="center_horizontal"

android:layout_marginTop="12dip"

android:text="关闭悬浮窗"

/>

<Button

android:id="@+id/back"

android:layout_width="100dip"

android:layout_height="40dip"

android:layout_gravity="center_horizontal"

android:text="返回"

/>

</LinearLayout>然后打开MainActivity.java,这是项目的主界面@Override

protected

void

onCreate(Bundle

savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button

startFloatWindow

=

(Button)

findViewById(R.id.start_float_window);

startFloatWindow.setOnClickListener(new

OnClickListener()

{

@Override

public

void

onClick(View

arg0)

{

//

开启服务

Intent

intent

=

new

Intent(MainActivity.this,

FloatWindowService.class);

startService(intent);

finish();

}

});

}MainActivity的代码就是对开启悬浮窗的按钮注册了一个点击事件,用于打开一个服务,然后关闭当前Activity。新建一个名为FloatWindowService的类,这个类继承自Servicepublic

class

FloatWindowService

extends

Service

{

/**

*

用于在线程中创建或移除悬浮窗。

*/

private

Handler

handler

=

new

Handler();

/**

*

定时器,定时进行检测当前应该创建还是移除悬浮窗。

*/

private

Timer

timer;

/**

*

不是绑定的开启服务

*/

@Override

public

IBinder

onBind(Intent

intent)

{

return

null;

}

/**

*

Service的生命周期中会调用此函数

*/

@Override

public

int

onStartCommand(Intent

intent,

int

flags,

int

startId)

{

//

开启定时器,每隔0.5秒刷新一次

if

(timer

==

null)

{

timer

=

new

Timer();

timer.scheduleAtFixedRate(new

RefreshTask(),

0,

500);

}

return

super.onStartCommand(intent,

flags,

startId);

}

/**

*

Service终止

*/

@Override

public

void

onDestroy()

{

super.onDestroy();

//

Service被终止的同时也停止定时器继续运行

timer.cancel();

timer

=

null;

}

/**

*

判断桌面上悬浮框状态

*

*

@author

sansung

*

*/

class

RefreshTask

extends

TimerTask

{

@Override

public

void

run()

{

//

当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。

if

(isHome()

&&

!MyWindowManager.isWindowShowing())

{

handler.post(new

Runnable()

{

@Override

public

void

run()

{

MyWindowManager

.createSmallWindow(getApplicationContext());

}

});

}

//

当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。

else

if

(!isHome()

&&

MyWindowManager.isWindowShowing())

{

handler.post(new

Runnable()

{

@Override

public

void

run()

{

MyWindowManager

.removeSmallWindow(getApplicationContext());

MyWindowManager

.removeBigWindow(getApplicationContext());

}

});

}

//

当前界面是桌面,且有悬浮窗显示,则更新内存数据。

else

if

(isHome()

&&

MyWindowManager.isWindowShowing())

{

handler.post(new

Runnable()

{

@Override

public

void

run()

{

MyWindowManager

.updateUsedPercent(getApplicationContext());

}

});

}

}

}

/**

*

判断当前界面是否是桌面

*/

private

boolean

isHome()

{

ActivityManager

mActivityManager

=

(ActivityManager)

getSystemService(Context.ACTIVITY_SERVICE);

List<RunningTaskInfo>

rti

=

mActivityManager.getRunningTasks(1);

//

如果在桌面,输出的是桌面的包名

//

不在桌面的话输出的是打开的app的包名

//

System.out.println("getHomes().contains(rti.get(0).topActivity.getPackageName())+++"+rti.get(0).topActivity.getPackageName());

return

getHomes().contains(rti.get(0).topActivity.getPackageName());

}

/**

*

获得属于桌面的应用的应用包名称

*

*

@return

返回包含所有包名的字符串列表

*/

private

List<String>

getHomes()

{

List<String>

names

=

new

ArrayList<String>();

PackageManager

packageManager

=

this.getPackageManager();

Intent

intent

=

new

Intent(Intent.ACTION_MAIN);

intent.addCategory(Intent.CATEGORY_HOME);

List<ResolveInfo>

resolveInfo

=

packageManager.queryIntentActivities(

intent,

PackageManager.MATCH_DEFAULT_ONLY);

for

(ResolveInfo

ri

:

resolveInfo)

{

//

桌面包名

//

System.out.println("ri.activityInfo.packageName++++"+ri.activityInfo.packageName);

names.add(ri.activityInfo.packageName);

}

return

names;

}

}FloatWindowService的onStartCommand方法中开启了一个定时器,每隔500毫秒就会执行RefreshTask。在RefreshTask当中,要进行判断,如果手机当前是在桌面的话,就应该显示悬浮窗,如果手机打开了某一个应用程序,就应该移除悬浮窗,如果手机在桌面的话,还应该更新内存使用百分比的数据。而当FloatWindowService被销毁的时候,应该将定时器停止,否则它还会一直运行。创建和移除悬浮窗,以及更新悬浮窗内的数据,都是由MyWindowManager这个类来管理的,比起直接把这些代码写在Activity或Service当中,使用一个专门的工具类来管理要好的多。不过要想创建悬浮窗,还是先要把悬浮窗的View写出来。新建一个名叫FloatWindowSmallView的类,继承自LinearLayout。新建一个名叫FloatWindowBigView的类,也继承自LinearLayout。FloatWindowSmallViewpublic

class

FloatWindowSmallView

extends

LinearLayout

{

/**

*

记录小悬浮窗的宽度

*/

public

static

int

viewWidth;

/**

*

记录小悬浮窗的高度

*/

public

static

int

viewHeight;

/**

*

记录系统状态栏的高度

*/

private

static

int

statusBarHeight;

/**

*

用于更新小悬浮窗的位置

*/

private

WindowManager

windowManager;

/**

*

小悬浮窗的参数

*/

private

WindowManager.LayoutParams

mParams;

/**

*

记录当前手指位置在屏幕上的横坐标值

*/

private

float

xInScreen;

/**

*

记录当前手指位置在屏幕上的纵坐标值

*/

private

float

yInScreen;

/**

*

记录手指按下时在屏幕上的横坐标的值

*/

private

float

xDownInScreen;

/**

*

记录手指按下时在屏幕上的纵坐标的值

*/

private

float

yDownInScreen;

/**

*

记录手指按下时在小悬浮窗的View上的横坐标的值

*/

private

float

xInView;

/**

*

记录手指按下时在小悬浮窗的View上的纵坐标的值

*/

private

float

yInView;

/**

*

构造函数

*

*

@param

context

*

上下文

*/

public

FloatWindowSmallView(Context

context)

{

super(context);

//

得到windowManager

windowManager

=

(WindowManager)

context

.getSystemService(Context.WINDOW_SERVICE);

LayoutInflater.from(context).inflate(R.layout.float_window_small,

this);

View

view

=

findViewById(R.id.small_window_layout);

viewWidth

=

view.getLayoutParams().width;

viewHeight

=

view.getLayoutParams().height;

TextView

percentView

=

(TextView)

findViewById(R.id.percent);

percentView.setText(MyWindowManager.getUsedPercentValue(context));

}

@Override

public

boolean

onTouchEvent(MotionEvent

event)

{

switch

(event.getAction())

{

case

MotionEvent.ACTION_DOWN:

//

手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度

xInView

=

event.getX();

yInView

=

event.getY();

xDownInScreen

=

event.getRawX();

yDownInScreen

=

event.getRawY()

-

getStatusBarHeight();

xInScreen

=

event.getRawX();

yInScreen

=

event.getRawY()

-

getStatusBarHeight();

break;

case

MotionEvent.ACTION_MOVE:

xInScreen

=

event.getRawX();

yInScreen

=

event.getRawY()

-

getStatusBarHeight();

//

手指移动的时候更新小悬浮窗的位置

updateViewPosition();

break;

case

MotionEvent.ACTION_UP:

//

如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。

if

(xDownInScreen

==

xInScreen

&&

yDownInScreen

==

yInScreen)

{

openBigWindow();

}

break;

default:

break;

}

return

true;

}

/**

*

将小悬浮窗的参数传入,用于更新小悬浮窗的位置。

*

*

@param

params

*

小悬浮窗的参数

*/

public

void

setParams(WindowManager.LayoutParams

params)

{

mParams

=

params;

}

/**

*

更新小悬浮窗在屏幕中的位置。

*/

private

void

updateViewPosition()

{

mParams.x

=

(int)

(xInScreen

-

xInView);

mParams.y

=

(int)

(yInScreen

-

yInView);

windowManager.updateViewLayout(this,

mParams);

}

/**

*

打开大悬浮窗,同时关闭小悬浮窗。

*/

private

void

openBigWindow()

{

MyWindowManager.createBigWindow(getContext());

MyWindowManager.removeSmallWindow(getContext());

}

/**

*

用于获取状态栏的高度。

*

*

@return

返回状态栏高度的像素值。

*/

private

int

getStatusBarHeight()

{

if

(statusBarHeight

==

0)

{

try

{

Class<?>

c

=

Class.forName("ernal.R$dimen");

Object

o

=

c.newInstance();

Field

field

=

c.getField("status_bar_height");

int

x

=

(Integer)

field.get(o);

statusBarHeight

=

getResources().getDimensionPixelSize(x);

}

catch

(Exception

e)

{

e.printStackTrace();

}

}

return

statusBarHeight;

}

}其中,对这个View的onTouchEvent事件进行了重写,用于实现拖动和点击的效果。如果发现用户触发了ACTION_DOWN事件,会记录按下时的坐标等数据。如果发现用户触发了ACTION_MOVE事件,则根据当前移动的坐标更新悬浮窗在屏幕中的位置。如果发现用户触发了ACTION_UP事件,会和ACTION_DOWN中记下的坐标对比,如果发现是相同的,则视为用户对悬浮窗进行了点击。点击小悬浮窗则打开大悬浮窗,然后我们来实现大悬浮窗的View。FloatWindowBigViewpublic

class

FloatWindowBigView

extends

LinearLayout

{

/**

*

记录大悬浮窗的宽度

*/

public

static

int

viewWidth;

/**

*

记录大悬浮窗的高度

*/

public

static

int

viewHeight;

public

FloatWindowBigView(final

Context

context)

{

super(context);

LayoutInflater.from(context).inflate(R.layout.float_window_big,

this);

View

view

=

findViewById(R.id.big_window_layout);

viewWidth

=

view.getLayoutParams().width;

viewHeight

=

view.getLayoutParams().height;

Button

close

=

(Button)

findViewById(R.id.close);

Button

back

=

(Button)

findViewById(R.id.back);

close.setOnClickListener(new

OnClickListener()

{

@Override

public

void

onClick(View

v)

{

//

点击关闭悬浮窗的时候,移除所有悬浮窗,并停止Service

MyWindowManager.removeBigWindow(context);

MyWindowManager.removeSmallWindow(context);

Intent

intent

=

new

Intent(getContext(),

FloatWindowService.class);

context.stopService(intent);

}

});

back.setOnClickListener(new

OnClickListener()

{

@Override

public

void

onClick(View

v)

{

//

点击返回的时候,移除大悬浮窗,创建小悬浮窗

MyWindowManager.removeBigWindow(context);

MyWindowManager.createSmallWindow(context);

}

});

}

}比起FloatWindowSmallView,FloatWindowBigView要简单的多,其中只有两个按钮,点击close按钮,将悬浮窗全部移除,并将Service终止。单击back按钮则移除大悬浮窗,重新创建小悬浮窗。现在两个悬浮窗的View都已经写好了,我们来创建MyWindowManagerpublic

class

MyWindowManager

{

/**

*

小悬浮窗View的实例

*/

private

static

FloatWindowSmallView

smallWindow;

/**

*

大悬浮窗View的实例

*/

private

static

FloatWindowBigView

bigWindow;

/**

*

小悬浮窗View的参数

*/

private

static

LayoutParams

smallWindowParams;

/**

*

大悬浮窗View的参数

*/

private

static

LayoutParams

bigWindowParams;

/**

*

用于控制在屏幕上添加或移除悬浮窗

*/

private

static

WindowManager

mWindowManager;

/**

*

用于获取手机可用内存

*/

private

static

ActivityManager

mActivityManager;

/**

*

创建一个小悬浮窗。初始位置为屏幕的右部中间位置。

*

*

@param

context

*

必须为应用程序的Context.

*/

public

static

void

createSmallWindow(Context

context)

{

//

创建windowmanager

WindowManager

windowManager

=

getWindowManager(context);

//

得到window长宽

int

screenWidth

=

windowManager.getDefaultDisplay().getWidth();

int

screenHeight

=

windowManager.getDefaultDisplay().getHeight();

if

(smallWindow

==

null)

{

smallWindow

=

new

FloatWindowSmallView(context);

if

(smallWindowParams

==

null)

{

smallWindowParams

=

new

LayoutParams();

smallWindowParams.type

=

LayoutParams.TYPE_PHONE;

smallWindowParams.format

=

PixelFormat.RGBA_8888;

smallWindowParams.flags

=

LayoutParams.FLAG_NOT_TOUCH_MODAL

|

LayoutParams.FLAG_NOT_FOCUSABLE;

smallWindowParams.gravity

=

Gravity.LEFT

|

Gravity.TOP;

smallWindowParams.width

=

FloatWindowSmallView.viewWidth;

smallWindowParams.height

=

FloatWindowSmallView.viewHeight;

smallWindowParams.x

=

screenWidth;

smallWindowParams.y

=

screenHeight

/

2;

}

//

配置

smallWindow.setParams(smallWindowParams);

//

在windowmanager中添加

windowManager.addView(smallWindow,

smallWindowParams);

}

}

/**

*

将小悬浮窗从屏幕上移除。

*

*

@param

context

*

必须为应用程序的Context.

*/

public

static

void

removeSmallWindow(Context

context)

{

if

(smallWindow

!=

null)

{

WindowManager

windowManager

=

getWindowManager(context);

windowManager.removeView(smallWindow);

smallWindow

=

null;

}

}

/**

*

创建一个大悬浮窗。位置为屏幕正中间。

*

*

@param

context

*

必须为应用程序的Context.

*/

public

static

void

createBigWindow(Context

context)

{

WindowManager

windowManager

=

getWindowManager(context);

int

screenWidth

=

windowManager.getDefaultDisplay().getWidth();

int

screenHeight

=

windowManager.getDefaultDisplay().getHeight();

if

(bigWindow

==

null)

{

bigWindow

=

new

FloatWindowBigView(context);

if

(bigWindowParams

==

null)

{

bigWindowParams

=

new

LayoutParams();

bigWindowParams.x

=

screenWidth

/

2

-

FloatWindowBigView.viewWidth

/

2;

bigWindowParams.y

=

screenHeight

/

2

-

FloatWindowBigView.viewHeight

/

2;

bigWindowParams.type

=

LayoutParams.TYPE_PHONE;

bigWindowParams.format

=

PixelFormat.RGBA_8888;

bigWindowParams.gravity

=

Gravity.LEFT

|

Gravity.TOP;

bigWindowParams.width

=

FloatWindowBigView.viewWidth;

bigWindowParams.height

=

FloatWindowBigView.viewHeight;

}

windowManager.addView(bigWindow,

bigWindowParams);

}

}

/**

*

将大悬浮窗从屏幕上移除。

*

*

@param

context

*

必须为应用程序的Context.

*/

public

static

void

removeBigWindow(Context

context)

{

if

(bigWindow

!=

null)

{

WindowManager

windowManager

=

getWindowManager(context);

windowManager.removeView(bigWindow);

bigWindow

=

null;

}

}

/**

*

更新小悬浮窗的TextView上的数据,显示内存使用的百分比。

*

*

@param

context

*

可传入应用程序上下文。

*/

public

static

void

updateUsedPercent(Context

context)

{

if

(smallWindow

!=

null)

{

TextView

percentView

=

(TextView)

smallWindow

.findViewById(R.id.percent);

percentView.setText(getUsedPercentValue(context));

}

}

/**

*

是否有悬浮窗(包括小悬浮窗和大悬浮窗)显示在屏幕上。

*

*

@return

有悬浮窗显示在桌面上返回true,没有的话返回false。

*/

public

static

boolean

isWindowShowing()

{

return

smallWindow

!=

null

||

bigWindow

!=

null;

}

/**

*

如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。

*

*

@param

context

*

必须为应用程序的Context.

*

@return

WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。

*/

private

static

WindowManager

getWindowManager(Context

context)

{

if

(mWindowManager

==

null)

{

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论