AppWidget开发

来源:互联网 发布:docker mysql 连接 编辑:程序博客网 时间:2024/06/16 05:55


一.开发Widget步骤

官方文档

1.添加Widget的描述文档(xml--对此Widget的相关设置,如宽和高,更新时间,显示LayoutActivity连接相关

代码如下:

<?xml version="1.0" encoding="utf-8"?>

<!--一定要声明为appwidget-provider,则说明是widget应用程序-->

<appwidget-provider

    android:minWidth="320dp"

    android:minHeight="80"

android:updatePeriodMillis="10000"

<!--widget的显示布局界面-->

    android:initialLayout="@/layout/activity_main">

</appwidget-provider>

 

2.创建一个继承自AppWidgetProvider的类,以实现对Widget的更新,其实AppWidgetProvider继承自BroadcastReceiver

3.定义组件layout文件

4.配置Manifest

(1)添加Receiver,选择继承自AppwidgetProvider的类

(2)为继承自AppwidgetProvider类的类添加元数据meta-data

<meta-data 

android:name="android.appwidget.provider"

<!--必须为android.appwidget.provider,描述此应用是个Widget程序-->

android:resource="@xml/widget_info"/>

<!--声明描述widgetxml文件位置-->

 

(3)添加Activity---如果有链接的话:

Intent-Filteraction--ACTION_APPWIDGET_CONFIGURE

Info(xml)文件中声明configuration = Activity所在包”

代码如下

    <application

        <receiver android:name="appWidget">

            <intent-filter>

<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>

            </intent-filter>

            <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/>

        </receiver>   </application>

 

二.代码如下

res/xml/widget_info:Widget程序配置文件

<?xml version="1.0" encoding="utf-8"?>

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"

    android:minWidth="80dp"

    android:minHeight="100dp"

android:updatePeriodMillis="1000"

<!--更新时间-,ms级别->

android:initialLayout="@layout/activity_main"

Android:configure = 一个配置widget的界面,不是继承自AppWidget的那个类>

<!--初始化布局界面,widget显示界面-->

</appwidget-provider>

相关属性

--android:minWidth/minHeight---声明widget的最小宽和高,参照下面的设计标准即可(4*1 3*3 。。。。。。)

--androidupdatePeriodMillis

声明好久更新一次widget,通过调用onUpdate(),单位是ms

不过,这个属性声明的时间实际上并不会太准,所以最好是使用一个alarm来实时更新widget,并且设置updatePeriodMillis=0即可。

--android:configure

配置widget的一个activity

--android:widgetCategory

声明appwidget是否能被放置到桌面和锁屏界面上--android:InitialKeyguardLayout

指定锁屏界面的layoutinitialLayout原理是一样的。

 

res/layout/activity_widget

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

  >

 

    <TextView

        android:id="@+id/showText"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignParentLeft="true"

        android:layout_alignParentRight="true"

        android:layout_alignParentTop="true"

        android:text="TextView" />

 

</RelativeLayout>

 

Src/com.ccp.wj.appwidget/AppWidget.java

public class appWidget extends AppWidgetProvider {

private static String []text = {"ccp", "wj", "chj"};

private static int count =0;

@Override

public void onUpdate(Context context, AppWidgetManager appWidgetManager,

int[] appWidgetIds) {

// TODO Auto-generated method stub

if(count >= 2)count = 0;

count++;

update( context,  appWidgetManager, appWidgetIds);

super.onUpdate(context, appWidgetManager, appWidgetIds);

}

 

private void update(Context context, AppWidgetManager appWidgetManager,

int[] appWidgetIds) {

// TODO Auto-generated method stub

RemoteViews rvs = new RemoteViews(context.getPackageName(),R.layout.activity_main);

rvs.setTextViewText(R.id.showText, text[count]);

      appManager.updateWidget(appWidgetIds, rvs);//更新

}

 

}

 

Widget与布局通过RemoteViewsonic通信

 

Res/AndroidManifest.xml

<application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <receiver android:name="appWidget">

            <intent-filter>

<!--public static final String ACTION_APPWIDGET_UPDATE

Added in API level 3

Sent when it is time to update your AppWidget.

This may be sent in response to a new instance for this AppWidget provider having been instantiated, the requested update interval having lapsed, or the system booting.

The intent will contain the following extras:

EXTRA_APPWIDGET_IDS

The appWidgetIds to update. This may be all of the AppWidgets created for this provider, or just a subset. The system tries to send updates for as few AppWidget instances as possible.

See Also

 AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)

Constant Value: "android.appwidget.action.APPWIDGET_UPDATE"

-->

                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

            </intent-filter>

            <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_info"/>

        </receiver>

注:只有在第一次安装程序时才会加载widget

二.设计Widget

竖直方向vertical

 

Ceil        piexl

4*1         320*100

4*2         320*200

3*3         240*300

2*2         160*200

水平方向horizon

 

4*1          424*74

3*3          318*222

2*2          212*148

三.指定一个Configure界面

即添加widget时,不是马上添加widget而是进入配置界面

1.在manifest中声明Activity

<activity android:name=".ExampleAppWidgetConfigure">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>

2.在appwidget_info.xml中声明android:configure

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    ... >
</appwidget-provider>

四.为AppWidget的控件添加相应事件

调用setOnClickPendingIntentint, PendintIntent

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }

五.定时刷新widget

因为要考虑到省电等问题,所以在widget_info.xm文档中标识的android:updatePeriodMillis的时间并不能保证及时刷新,一般来说是30分钟,我们一般在onUpdate()中添加一个Service来保证widget的实时刷新,具体实现方法如在其他Activity中更新widget一样

更新widget

1.首先,从运行的Activity中获取widgetid

Intent intent = getIntent();Bundle extras = intent.getExtras();if (extras != null) {
    mAppWidgetId = extras.getInt(
            AppWidgetManager.EXTRA_APPWIDGET_ID, 
            AppWidgetManager.INVALID_APPWIDGET_ID);}

2.配置appWidget的设置

3.配置完毕,通过getInstancecontext)获取一个AppWidgetManager的实例。

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

4.通过updateAppWidget(int ,RemoteViews)RemoteViews更新appWidget

RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);

5.最后创建一个返回Intent,设置一个Activity Result并且finish掉这个Activity

Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();

Tip:当你的configure Activity第一次打开,设置ActivityRESULT_CANCEL,这样,如果用户在配置之前就退出了,那么app widget host就会提醒这个配置取消了,并且不会被添加。

2.实现实时更新

--onUpdate()中添加一个alarm,定时更发送广播并在广播接收器中更新widget

代码如下

appWidgetProvider

@Override

public void onUpdate(Context context, AppWidgetManager appWidgetManager,

int[] appWidgetIds) {

// TODO Auto-generated method stub

super.onUpdate(context, appWidgetManager, appWidgetIds);

Log.d(tag"onUpdate--widget刷新");

final int N = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider

updateWidget(context,N,appWidgetManager,appWidgetIds);

//每分钟刷新一次时间

if(setCount<1)

setRefershWidgetTime(appWidgetIds);

     }

/**

 * 定时刷新widget的时间

 * @param appWidgetIds 

 */

public void setRefershWidgetTime(int[] appWidgetIds){

AlarmManager am = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);

Intent intent = new Intent(StringHelper.WIDGET_UPDATE);

intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);

PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);

int triggerTime = 60*1000;

am.setRepeating(AlarmManager.RTC, System.currentTimeMillis()+triggerTime, triggerTime, pi);

setCount++;

}

BroadcastReceiver

package com.ccp.wj.doit.receiver;

import com.ccp.wj.doit.utils.DateUtils;

import com.ccp.wj.doit.utils.StringHelper;

import com.ccp.wj.doit.widget.AppWidgetConfigureActivity;

import com.ccp.wj.doitweatherforcast.R;

import android.app.PendingIntent;

import android.appwidget.AppWidgetManager;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.os.Bundle;

import android.widget.RemoteViews;

public class WidgetUpdateReceiver extends BroadcastReceiver {

private int h,m;

private int[]imgId=new int[4];

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

Bundle b = intent.getExtras();

if(b!=null){

int appWidgetIds[]= b.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);

updateWidgetTime(context,appWidgetIds);

}

}

private void updateWidgetTime(Context context, int[] appWidgetIds) {

// TODO Auto-generated method stub

int N = appWidgetIds.length;

for (int i=0; i<N; i++) {

            int appWidgetId = appWidgetIds[i];

            widgetInit();

            // Create an Intent to launch ExampleActivity

            // Get the layout for the App Widget and attach an on-click listener

            // to the button

            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_4x1);

            views.setImageViewResource(R.id.clock_0, imgId[0]);

            views.setImageViewResource(R.id.clock_1, imgId[1]);

            views.setImageViewResource(R.id.clock_2, R.drawable.widget_white);

            views.setImageViewResource(R.id.clock_3, imgId[2]);

            views.setImageViewResource(R.id.clock_4, imgId[3]);

            //get the appWidgetManager

            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

            appWidgetManager.updateAppWidget(appWidgetId, views);

 }

}

 

private void widgetInit() {

// TODO Auto-generated method stub

h = DateUtils.getHours();

 m = DateUtils.getMinutes();

 String hour[]=new String[2],minutues[]=new String[2],hh,mm;

if(h < 10){

imgId[0]=StringHelper.clock_img_id[0];

imgId[1]=StringHelper.clock_img_id[h];

}

else{

hh=String.valueOf(h);

hour[0]=hh.substring(0, 1);

hour[1]=hh.substring(1);

imgId[0] = StringHelper.clock_img_id[Integer.parseInt(hour[0])];

imgId[1]=StringHelper.clock_img_id[Integer.parseInt(hour[1])];

}

if(m<10){

imgId[2]=StringHelper.clock_img_id[0];

imgId[3]=StringHelper.clock_img_id[m];

}

else{

mm = String.valueOf(m);

minutues[0]=mm.substring(0, 1);

minutues[1]=mm.substring(1);

imgId[2] = StringHelper.clock_img_id[Integer.parseInt(minutues[0])];

imgId[3]=StringHelper.clock_img_id[Integer.parseInt(minutues[1])];

}

}

 

}

 

六.多个widget放置在桌面时响应事件混乱

 当在屏幕上有多个widget时,无论在哪个widget上点击,响应事件的总是最后一个widget,原因就是PendingIntent传值时出现了错误,每次添加的新PendingIntent把原来的PendingIntent的替换掉了、

PendingIntent pi = PendintIntent.getBroadcast(context, 0, intent,0);

我们固定把第二个参数设置了为0.而第二个参数表示的是requestCode表示发送器的私有请求码,相当于pendingIntent的一个IdPendingIntent会根据这个请求码来判断是否存在相同的PendingIntent,如果存在则替换,所以出现了以上情况。

建议PendingIntent pendingIntent = PendingIntent.getBroadcast(context,appWidgetId,intent,0);

七.获取widget的所有id--appWidgetIds

appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName("com.ccp.wj.doitweatherforcast","com.ccp.wj.doit.widget.DoItWeatherWidget"));

 

0 0
原创粉丝点击