android桌面widget怎么设置它大小与屏幕同宽啊,理解与应用Android桌面组件AppWidget...

一、概念

首先要区分widget和AppWidget这两个概念。

1、Widget

widget可以直译为小部件,它在Android中代表视图的概念,如TextView、Button、EditText等widget视图控件,及LinearLayout等视图布局。

6903035d10bc3ed82f689a3b5b6c7af1.png

2、AppWidget

AppWidget是放置在手机屏幕的桌面小组件应用,如时钟、日历、天气等组件,与一般应用程序有所不同。一般应用虽也可以以图标的形式(快捷方式)放在桌面,但必须点击运行和查看;而AppWidget一般不须点击即直观呈现其主要内容。当然,AppWidget也可以被设置为点击打开其它屏幕或应用等。

而且,AppWidget可以被定时更新,如日历每天更新,时钟每分钟更新等。当然,也可以在AppWidget的视图界面中加入类似刷新的小按钮,以进行实时更新,如天气预报。

一般在提到Widget部件或Widget程序时,指的是AppWidget;如果说到widget控件,则可能是指视图控件,如Button等。

3、操作

通过在桌面(HomeScreen)中长按,在弹出的对话框中选择AppWidget部件来进行创建;或者在应用程序列表的AppWidget程序列表中选择并长按来创建。同一个AppWidget部件可以在桌面同时创建多个。每新建一个,实际上是生成了一个新的AppWidget实例。

要删除桌面的Widget部件,只需长按并拖动到垃圾箱即可。

二、一个简单的AppWidget应用

1、简单AppWidget组成

一个简单的AppWidget应用只需包括以下部分:

AppWidgetProviderInfo对象:

这个对象为AppWidget提供元数据,包括布局、更新频率等信息,这个对象定义在xml文件中,不需要自己编写,由系统根据XML文件生成。

AppWidgetProvider类:

5c22f2201f22790708d09dc8ac21ea5a.png

如图所示,AppWidgetProvider类,继承自BroadcastReceiver,可以接收并处理广播事件。这个类定义了AppWidget的基本生命周期函数:

onReceive(Context, Intent)   接收广播事件。

onUpdate(Context , AppWidgetManager, int[] appWidgetIds)  到达指定的更新时间或用户向桌面添加widget时调用;实际是接受并处理“android.appwidget.action.APPWIDGET_UPDATE”广播事件。appWidgetIds保存着已创建的各(桌面)AppWidget实例编号。在onUpdate方法中可以依次更新所有实例的界面内容。

onEnabled(Context)  当AppWidget实例第一次被创建时调用

onDeleted(Context, int[] appWidgetIds)  当一个AppWidget实例被删除时调用

onDisabled(Context)  当最后一个AppWidget实例被删除时调用

2、一个简单应用开发

该应用很简单,只是在桌面显示一行文字。

(1)应用的界面布局文件res/layout/appwidget_provider_layout.xml:

4ced8760e143dc2f4993c929aa8b0f85.png

(2)应用的元数据定义文件res/xml/appwidget_provider.xml:

296cebd7d6bb29fd687ea0aedec6d14b.png

(3)Widget实例提供程序SimpleWidgetProvider.java文件:

package com.example.simpleappwidget;

import android.appwidget.AppWidgetManager;

import android.appwidget.AppWidgetProvider;

import android.content.Context;

import android.util.Log;

import android.widget.RemoteViews;

import com.example.simpleappwidget.R;

public class SimpleWidgetProvider extends AppWidgetProvider {

private String TAG = "widgetexample";

周期更新时调用

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

{

final int N = appWidgetIds.length;

Log.i(TAG,String.valueOf(N));

for (int i = 0; i 

{

int appWidgetId = appWidgetIds[i];

String message = "目前有"+N+"个AppWidget实例";

构建RemoteViews对象来对桌面部件进行更新

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

更新文本内容,指定布局的组件

views.setTextViewText(R.id.appwidget_text, message);

将RemoteViews的更新传入AppWidget进行更新

appWidgetManager.updateAppWidget(appWidgetId, views);

}

}

}

该应用比较简单,只是为了说明程序的结构。Provider类中仅提供了更新事件处理方法。运行界面如下图:

8afb5018446dca616ba4c64d41861d6d.png

可以创建多个实例:

90a3774a94056de153345d312233a64d.png

但并没有如估计的显示“目前有2个实例”。

关闭模拟器并重新启动打开,才显示有两个实例:

994d0214edf051b613d4112751465527.png

问题解决:前面更新多个实例的问题,后面通过将以实例ID为参数逐一修改Widget组件实例的以下方法:

appWidgetManager.updateAppWidget(appWidgetId, views);改为调用组件管理器的修改所有小组件实例的方法:

ComponentName myComponentName = new ComponentName(context, SimpleWidgetProvider.class);appWidgetManager.updateAppWidget(myComponentName,views);

3、为AppWidget程序添加按钮事件处理

天气预报等桌面组件有类似功能,即点击一个小图标(按钮)刷新数据显示。这里只是简单模拟一下类似功能。

点击按钮刷新数据,可以有多种方式实现。如点击按钮打开一个Activity、点击发送广播消息、点击启动一个服务等。下面首先看一下点击打开Activity的关键代码实现:

(1)按钮事件处理可以在SimpleWidgetProvider类的onUpdate方法中实现:

......

for (int i = 0; i 

{

int appWidgetId = appWidgetIds[i];

String message = "目前有"+N+"个AppWidget实例";

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

views.setTextViewText(R.id.appwidget_text, message);

为按钮绑定点击事件处理器

Intent intent = new Intent(context, MyActivity.class);

intent.putExtra("appWidgetId", appWidgetId);

Log.i(TAG,"ID:"+(intent.getExtras()).getInt("appWidgetId"));

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,

PendingIntent.FLAG_CANCEL_CURRENT);

views.setOnClickPendingIntent(R.id.mybutton, pendingIntent);

将RemoteViews的更新传入AppWidget进行更新

appWidgetManager.updateAppWidget(appWidgetId, views);

}

......

需要指出的是如图所示的PendingIntent的几个常量值(用于getActivity等方法的参数):

c80e62d7e5fa82811beb91b8e133c3aa.png这里因为Intent带有数据,使用了PendingIntent.FLAG_CANCEL_CURRENT。

(2)MyActivity类的代码:

......

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_my);

Bundle bundle = getIntent().getExtras();

int appWidgetID = bundle.getInt("appWidgetId");

Log.i(TAG,"another ID:"+appWidgetID);

final Context context = this;

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

更新文本内容,指定布局的组件

views.setTextViewText(R.id.appwidget_text, "点击按钮更新内容");

取得AppWidgetManager实例

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

appWidgetManager.updateAppWidget(appWidgetID, views);

this.finish();

}

......

上述功能也可以通过广播消息进行处理。因为AppWidgetProvider本身就继承自BroadcastReceiver,所以可以在SimpleWidgetProvider类的onReceive方法中实现对自定义消息的处理。关键代码如下:

(1)SimpleWidgetProvider类代码:

......

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

{

final int N = appWidgetIds.length;

Log.i(TAG,String.valueOf(N));

for (int i = 0; i 

{

int appWidgetId = appWidgetIds[i];

......

Intent intent = new Intent("update_appwidget_textview");

intent.putExtra("appWidgetId", appWidgetId);

PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,

PendingIntent.FLAG_CANCEL_CURRENT);

点击按钮将触发广播,当前接收器将即时接收和处理广播消息

views.setOnClickPendingIntent(R.id.mybutton, pendingIntent);

appWidgetManager.updateAppWidget(appWidgetId, views);

}

}

......

@Override

public void onReceive(Context context, Intent intent)

{

String action = intent.getAction();

if(action.equals("update_appwidget_textview"))

{

Log.i(TAG,"update_appwidget_textview");

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

views.setTextViewText(R.id.appwidget_text, "点击按钮更新内容");

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

int appWidgetId = (intent.getExtras()).getInt("appWidgetId");

appWidgetManager.updateAppWidget(appWidgetId, views);

}

else

super.onReceive(context, intent);

}

......

(2)AndroidManifest.xml文件:

67f4405ccc939e4d3567c89c4e8c1056.png

(3)应用的界面布局文件res/layout/appwidget_provider_layout.xml:

d97fa55808665b171a9fa51c5b93fc45.png

(4)应用的元数据定义文件res/xml/appwidget_provider.xml:

764e405056925a5b5bafe8d74f634cd0.png

示例程序代码下载

另外,从资料中还查到一种利用ComponentName类修改AppWidget实例的方法,只需对上面代码稍加改动:

onUpdate方法:

for (int i = 0; i 

{

为了看到每次调用该方法时内容的变化

String message = System.currentTimeMillis()+"";

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

views.setTextViewText(R.id.appwidget_text, message);

......

Intent intent = new Intent("update_appwidget_textview");

PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,

PendingIntent.FLAG_CANCEL_CURRENT);

views.setOnClickPendingIntent(R.id.mybutton, pendingIntent);

appWidgetManager.updateAppWidget(appWidgetId, views);

}

onReceive方法:

......

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

ComponentName componentName = new ComponentName(context, SimpleWidgetProvider.class);

appWidgetManager.updateAppWidget(componentName, views);

......

使用广播消息处理的方式当然也可以另外创建一个接收器,不再具体分析。示例程序下载

4、一个较实用的例子

例子比较简单,只是在HomeScreen桌面实时显示时间。

(1)SimpleWidgetProvider类关键代码(onUpdate方法):

......

int appWidgetId = appWidgetIds[i];

Intent intent = new Intent("com.example.updatetime");

intent.putExtra("appWidgetId", appWidgetId);

context.startService(intent);

......

(2)ExampleService类代码:

......

static int appWidgetId;

static RemoteViews views;

static AppWidgetManager appWidgetManager;

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

views = new RemoteViews(this.getPackageName(),

R.layout.appwidget_provider_layout);

appWidgetManager = AppWidgetManager.getInstance(this);

appWidgetId = (intent.getExtras()).getInt("appWidgetId");

new TimeThread().start();

return super.onStartCommand(intent, flags, startId);

}

class TimeThread extends Thread

{

@Override

public void run ()

{

do{

try

{

Thread.sleep(1000);

views.setTextViewText(R.id.appwidget_text, DateFormat.format("hh:mm:ss",

System.currentTimeMillis()));

appWidgetManager.updateAppWidget(appWidgetId, views);

}

catch (InterruptedException e)

{

e.printStackTrace();

}

} while(true);

}

}

......

(5)时钟显示程序的另一种解决方法

主要变化是使用android.content.Intent.ACTION_TIME_TICK时钟服务,主要代码如下:

SimpleWidgetProvider类:

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

{

final int N = appWidgetIds.length;

Log.i(TAG,String.valueOf(N));

String message = N+"个Widget实例";

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

views.setTextViewText(R.id.appwidget_text, message);

MyReceiver myReceiver = new MyReceiver();

IntentFilter myFilter = new IntentFilter();

myFilter.addAction(android.content.Intent.ACTION_TIME_TICK);

context.getApplicationContext().registerReceiver(myReceiver, myFilter);

ComponentName myComponentName = new ComponentName(context, SimpleWidgetProvider.class);

appWidgetManager.updateAppWidget(myComponentName,views);

}

public static void updateWidget(Context context,RemoteViews views)

{

ComponentName myComponentName = new ComponentName(context, SimpleWidgetProvider.class);

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

appWidgetManager.updateAppWidget(myComponentName,views);

}

MyReceiver类:

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if(action.equals("android.intent.action.TIME_TICK"))

{

RemoteViews views = new RemoteViews(context.getPackageName(),

R.layout.appwidget_provider_layout);

views.setTextViewText(R.id.appwidget_text, DateFormat.format("hh:mm",

System.currentTimeMillis()));

SimpleWidgetProvider.updateWidget(context, views);

}

}

需要说明的是ACTION_TIME_TICK这个广播消息是系统时钟消息,该消息只能由系统以每分钟一次的形式发送。不能在自定义类中通过sendbroadcast方法发出,否则会抛出“Permission Denial: not allowed to send broadcast android.intent.action.TIME_TICK”异常。

而且,程序中不能通过在manifest.xml里注册的方式接收到这个广播,只能在代码里通过registerReceiver()方法注册。

SDK文档原文内容:Broadcast Action: The current time has changed. Sent every minute. You can not receive this through components declared in manifests, only by exlicitly registering for it withContext.registerReceiver().

通过测试,发现在配置文件中设置小组件更新周期不起作用。android:updatePeriodMillis="1000"设置一秒更新一次,完全没有反应。只在长按程序生成桌面组件时,重新启动模拟器后,才会调用onUpdate方法。

以上测试验证了其它资料中提到新版本的Android屏蔽了小组件更新周期设置的说法。如果需要修改组件界面,需要在程序中使用如updateAppWidget(componentName, views)语句主动更新。

参考文章:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值