android widget 开发实例

来源:互联网 发布:xilinx ise mac 编辑:程序博客网 时间:2024/05/21 10:30

本文转载自silenceburn 大师 

桌面便签软件是android上常用软件的一种,比如比较早的Sticky Note,就曾非常流行,

Sticky Note的介绍可以参见  http://www.tompda.com/c/article/11778/  

而实际上使用android平台对widget开发的支持,桌面便签类软件是非常易于开发的。

本文通过逐步实现一个简单的桌面便签软件,和大家分享进行widget开发的过程和方法。


1.MyNote的最终实现效果

为了提起大家阅读本文的兴趣,先介绍一下最终实现的效果。

首先可以通过桌面增加我们的MyNote小部件,如下图所示:

图中的“我的便签”就是我们之后将要开发的便签程序。

点击后启动添加日志界面,如下图所示:

输入便签内容后,可以点击下面所列的四种图标之一作为便签图标。

比如点击第一个后,桌面上就会添加一个便签:

点击桌面上的便签,可以再次对便签内容进行修改,并更换图标。

桌面上可以同时存在多个便签,并可以分别进行修改。

如下图所示,我们将刚才创建的便签的图标修改一下,并新增了一个便签:

每个便签的内容都是分别独立保存的,可以随时点击桌面图标修改。


2.开发方式

开发的目的和追求的效果已经十分清楚了,首先我们确定一下开发方式。

在本文中,将采取一种渐进式的开发,也就是说不会一口气从头做到尾。

而是分为好几个阶段。每个阶段都完成一定的目标,然后下个阶段增加更多的功能,

每个阶段都离最终目标更进一步,OK,你可以说这是一次敏捷开发 :)

第一个阶段,首先我们会搭建一个widget原型程序,

它是完全可以运行的,可以创建桌面widget。

第二个阶段,我们改进 widget 配置Activity 部分的实现

使其具备创建便签的功能

第三个阶段,我们改进 widget 点击响应部分的实现,

使其具备修改便签的功能


3.搭建widget原型程序

本节我们会做一个最简单的widget程序原型,但是它是可以运行的。

一般来说 widget 程序由以下部分组成:

a. AppWidgetProvider 的实现 

b. widget外观布局定义文件

c. 新增widget时的配置Activity的实现(可选)

d. widget 参数配置文件 

以下分别讲解

a. AppWidgetProvider 的实现 

首先我们新建一个android工程起名为MyNote,然后修改 MyNote.java 的代码,

使MyNote继承自 AppWidgetProvider ,并重写 onUpdate 和 onDeleted 方法。

其中onUpdate 会在widget创建及被更新时调用, onDeleted 会在widget被删除时调用。

目前我们不需要在这里实现任何功能,只是简单的记录日志以便我们观察其运行,编写好的代码如下:

 

view plain copy to clipboard print ?
  1. package com.silenceburn;  
  2.   
  3. import android.appwidget.AppWidgetManager;  
  4. import android.appwidget.AppWidgetProvider;  
  5. import android.content.Context;  
  6. import android.util.Log;  
  7.   
  8. public  class  MyNote  extends  AppWidgetProvider {  
  9.     /** Called when the activity is first created. */  
  10.       
  11.     final String mPerfName =  "com.silenceburn.MyColorNoteConf" ;  
  12.   
  13.     @Override  
  14.     public  void  onUpdate(Context context, AppWidgetManager appWidgetManager,  
  15.             int[] appWidgetIds) {  
  16.         // TODO Auto-generated method stub  
  17.         final  int  N = appWidgetIds.length;  
  18.         for ( int  i =  0 ; i < N; i++) {  
  19.             int appWidgetId = appWidgetIds[i];  
  20.             Log.i("myLog" "this is ["  + appWidgetId +  "] onUpdate!" );  
  21.   
  22.         }  
  23.     }  
  24.   
  25.     @Override  
  26.     public  void  onDeleted(Context context,  int [] appWidgetIds) {  
  27.         // TODO Auto-generated method stub  
  28.         final  int  N = appWidgetIds.length;  
  29.         for ( int  i =  0 ; i < N; i++) {  
  30.             int appWidgetId = appWidgetIds[i];  
  31.             Log.i("myLog" "this is ["  + appWidgetId +  "] onDelete!" );  
  32.         }  
  33.     }  
  34.   
  35. }  

b. widget外观布局定义文件

我们需要为widget编写一个外观布局文件,在本示例中,布局非常简单,只需要一个imageView即可

编写好的 my_note_widget.xml 文件如下:

 

view plain copy to clipboard print ?
  1. <?xml   version = "1.0"   encoding = "utf-8" ?>   
  2. <ImageView   xmlns:android = "http://schemas.android.com/apk/res/android"   
  3.     android:id= "@+id/my_widget_img"   
  4.     android:layout_width= "wrap_content"   
  5.     android:layout_height= "wrap_content"   
  6.     android:src= "@drawable/sketchy_paper_008"   
  7.     android:clickable= "true" />   

 

这里用到了一个外部图片 sketchy_paper_008.png,来源于网络,感谢图片原作者。

可以到  http://dryicons.com/free-icons/preview/sketchy-paper-icons/  打包下载。

(  注意下载下来的包中的文件名可能和我写的程序中的命名有差异,请注意自行调整。)

c. 新增widget时的配置Activity的实现(可选)

android平台为widget提供一个配置界面的功能,我们可以自定义一个Activity,

在widget参数配置文件中配置好相关参数后,此Activity会在用户新增widget时自动调用。

一般来说,这个配置界面的作用是用户新建widget时,让用户配置widget的一些属性,比如颜色、大小等等。

但是在我们的这个示例程序中,我们用它来当做创建便签的地方!

不过本节只是先实现一个原型程序,所以暂时不做处理,我们只是新建一个Activity即可。

新建名为MyNoteConf的Activity,重写onCreate方法,在OnCreate方法中,

由于这个Activity是由系统在新增widget时自动调用的,

所以我们可以用getIntent获取到传入的widgetId。可以判断其是否是一个有效的widgetId,

最后我们必须返回一个RESULT_OK的Intent,并结束当前Activity,系统才会认为配置成功,在桌面上放置这个widget。

如果返回RESULT_CANCELED,系统会认为配置失败,终止widget的创建过程。

编写好的MyNoteConf的代码如下:

 

view plain copy to clipboard print ?
  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.appwidget.AppWidgetManager;  
  5. import android.content.Intent;  
  6. import android.os.Bundle;  
  7. import android.util.Log;  
  8.   
  9. public  class  MyNoteConf  extends  Activity {  
  10.       
  11.     int mAppWidgetId;  
  12.       
  13.     @Override  
  14.     protected  void  onCreate(Bundle savedInstanceState) {  
  15.         // TODO Auto-generated method stub  
  16.         super.onCreate(savedInstanceState);  
  17.           
  18.         Log.i("myLog", " on WidgetConf ... " );  
  19.           
  20.         setResult(RESULT_CANCELED);  
  21.           
  22.         // Find the widget id from the intent.  
  23.         Intent intent = getIntent();  
  24.         Bundle extras = intent.getExtras();  
  25.         if (extras !=  null ) {  
  26.             mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,  
  27.                     AppWidgetManager.INVALID_APPWIDGET_ID);  
  28.         }  
  29.   
  30.         // If they gave us an intent without the widget id, just bail.  
  31.         if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {  
  32.             finish();  
  33.         }  
  34.           
  35.         // return OK  
  36.         Intent resultValue = new Intent();  
  37.         resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,  
  38.                 mAppWidgetId);  
  39.           
  40.         setResult(RESULT_OK, resultValue);  
  41.         finish();  
  42.     }  
  43. }  

d. widget 参数配置文件

最后我们需要编写一个widget参数配置文件,将布局文件、配置Activity关联起来。

我们在res下新建目录xml,在xml目录下新增文件 my_note_widget.xml ,编写如下:

 

view plain copy to clipboard print ?
  1. <appwidget-provider   xmlns:android = "http://schemas.android.com/apk/res/android"   
  2.     android:minWidth= "72dp"   android:minHeight = "72dp"   
  3.     android:updatePeriodMillis= "86400000"   android:initialLayout = "@layout/my_note_widget"   
  4.     android:configure= "com.silenceburn.MyNoteConf" >   
  5. </appwidget-provider >   

其中 minWidth minHeight 用来指定widget的大小,如果我们只占用一个格子,也就是俗称的1X1,

那么72dp的长宽是android平台推荐的一个最佳实践值。

然后用 initialLayout 参数关联了我们编写好的 layout 文件,

用 configure 参数关联了我们编写好的配置用Activity:MyNoteConf,

此外还有一个参数 updatePeriodMills 指定widget的刷新周期,

从省电角度考虑,一般都把此值设置的比较大,如果一定要对widget做周期性的事情,可以使用AlarmManager。

至此所有widget的要素都已经准备好,我们运行一下来看看吧。


4.运行widget原型程序

为了运行widget,我们还需要修改一下 AndroidManifest.xml 来声明我们的widget。

声明一个receiver,过滤 android.appwidget.action.APPWIDGET_UPDATE ,

并且用metadata关联到我们自己编写的 appWidgetProvider 实现。

声明一个activity关联到我们的配置类 MyNoteConf,过滤 android.appwidget.action.APPWIDGET_CONFIGURE。

最后修改一下应用图标,此图标会出现在系统的新增widget列表中。

编写好的AndroidManifest.xml 如下:

 

view plain copy to clipboard print ?
  1. <?xml   version = "1.0"   encoding = "utf-8" ?>   
  2. <manifest   xmlns:android = "http://schemas.android.com/apk/res/android"   
  3.     package= "com.silenceburn"   android:versionCode = "1"   android:versionName = "1.0" >   
  4.     <application   android:icon = "@drawable/sketchy_paper_008"   
  5.         android:label= "@string/app_name" >   
  6.         <receiver   android:name = ".MyNote" >   
  7.             <intent-filter >   
  8.                 <action   android:name = "android.appwidget.action.APPWIDGET_UPDATE"   />   
  9.             </intent-filter >   
  10.             <meta-data   android:name = "android.appwidget.provider"   
  11.                 android:resource= "@xml/my_note_widget"   />   
  12.         </receiver >   
  13.   
  14.         <activity   android:name = ".MyNoteConf" >   
  15.             <intent-filter >   
  16.                 <action   android:name = "android.appwidget.action.APPWIDGET_CONFIGURE"   />   
  17.             </intent-filter >   
  18.         </activity >   
  19.     </application >   
  20. </manifest >    

至此原型程序全部开发完成,运行一下看看效果吧!

在桌面上长点,可以选择我们刚刚写的原型widget“MyNote”了,

选择后出现我们定义的配置界面MyNoteConf,

但是由于我们在onCreate中finish了,所以是一闪而过的。

之后MyNote就出现在桌面上了。

我们可以随便拖动它,或者把它丢进垃圾箱,观察一下日志输出。


上半部分总结

上半部分主要完成了一个widget的原型,它没有任何业务功能,

但是已经是一个可以运行的骨架了。

在下半部分中我们为它添加血和肉,让它真正具备业务功能。

希望大家喜欢这种先写骨架,再逐步丰富的开发方式 :)

 

5. 利用widget的配置Activtiy,实现新增便签功能

由于配置Activity由系统确保在新增widget时一定会调用,因此我们正好用此界面完成新增便签的功能。

我们同样采用渐进式的开发方式,分为如下几个阶段

a. 实现layout

b. 实现按键点击

c. 实现数据存储

以下分步讲解

a.实现layout

首先我们要为配置Activity定制一个layout,用于实现新增便签功能。观察第一节中的最终效果图,

在我们的Layout上,主要由 提示文本TextView 、 编辑文本框EditText 、四个图片按钮ImageButton  三部分组成。

编写一个layout文件起名为 my_note_conf.xml 放在layout文件夹下,在文件中,

首先用一个垂直的LinearLayout 把这三部分组织起来,通过使用weight权重设置,使EditText自动扩大到占满屏幕。

然后把四个图片按钮ImageButton 用一个水平的内嵌LinerLayout组织起来,并调整 gravity、margin 等属性使其更加美观。

编写好的layout文件如下:

 

view plain copy to clipboard print ?
  1. <?xml   version = "1.0"   encoding = "utf-8" ?>   
  2. <LinearLayout   android:orientation = "vertical"   
  3.     android:layout_width= "fill_parent"   android:layout_height = "fill_parent"   
  4.     xmlns:android= "http://schemas.android.com/apk/res/android" >   
  5.     <TextView   android:layout_height = "wrap_content"   android:text = "@string/note_title"   
  6.         android:layout_width= "wrap_content"   android:padding = "10dp" > </ TextView >   
  7.     <LinearLayout   android:layout_height = "fill_parent"   
  8.         android:layout_width= "fill_parent"   android:layout_weight = "1" >   
  9.         <EditText   android:id = "@+id/EditText02"   android:layout_width = "fill_parent"   
  10.             android:layout_height= "fill_parent"   android:gravity = "left"   
  11.             android:hint= "@string/edithint" > </ EditText >   
  12.     </LinearLayout >   
  13.     <LinearLayout   android:layout_height = "fill_parent"   
  14.         android:layout_width= "fill_parent"   android:layout_weight = "2"   
  15.         android:gravity= "center" >   
  16.         <ImageButton   android:id = "@+id/ImageButton01"   
  17.             android:layout_width= "72dp"   android:layout_height = "72dp"   
  18.             android:src= "@drawable/sketchy_paper_003"   android:layout_margin = "3dp" > </ ImageButton >   
  19.         <ImageButton   android:id = "@+id/ImageButton02"   
  20.             android:layout_width= "72dp"   android:layout_height = "72dp"   
  21.             android:src= "@drawable/sketchy_paper_004"   android:layout_margin = "3dp" > </ ImageButton >   
  22.         <ImageButton   android:id = "@+id/ImageButton03"   
  23.             android:layout_width= "72dp"   android:layout_height = "72dp"   
  24.             android:src= "@drawable/sketchy_paper_007"   android:layout_margin = "3dp" > </ ImageButton >   
  25.         <ImageButton   android:id = "@+id/ImageButton04"   
  26.             android:layout_width= "72dp"   android:layout_height = "72dp"   
  27.             android:src= "@drawable/sketchy_paper_011"   android:layout_margin = "3dp" > </ ImageButton >   
  28.     </LinearLayout >   
  29. </LinearLayout >   

注意其中调用了外部图片,下载参考第3节的b小节。

还调用了strings.xml中的字符串定义。关于字符串定义常量,你可以用values-zh实现国际化,此处不再敷述。

至此layout编写完成,记得在配置Activity中增加 setContentView(R.layout.my_note_conf); 语句指定使用该layout文件。

b.实现按键点击

接下来我们实现四个ImageButton上的按键点击事件,由于按键事件基本相同,

因此我们只编写一个OnClickListener,然后把它绑定到四个按钮上去。

在OnClickListener中,首先我们获取被点击的按钮的id,由此得知用户希望使用那一个图片作为widget图标,

然后获取 RemoteViews 关联到我们的widget,设置widget的imageSrc为新的图片,

设置完成后需要获取AppWidgetManager,对指定的widget进行更新,才能使设置生效。

最后不要忘记把onCreate中返回RESULT_OK和finish的代码移到OnClickListener中来。

b小节部分编写完成后的代码如下:

 

view plain copy to clipboard print ?
  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.appwidget.AppWidgetManager;  
  5. import android.content.Intent;  
  6. import android.os.Bundle;  
  7. import android.util.Log;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.widget.ImageButton;  
  11. import android.widget.RemoteViews;  
  12.   
  13. public  class  MyNoteConf  extends  Activity {  
  14.   
  15.     int mAppWidgetId;  
  16.     ImageButton mImBtn1, mImBtn2, mImBtn3, mImBtn4;  
  17.   
  18.     @Override  
  19.     protected  void  onCreate(Bundle savedInstanceState) {  
  20.         // TODO Auto-generated method stub  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.my_note_conf);  
  23.         Log.i("myLog" " on WidgetConf ... " );  
  24.   
  25.         setResult(RESULT_CANCELED);  
  26.         // Find the widget id from the intent.  
  27.         Intent intent = getIntent();  
  28.         Bundle extras = intent.getExtras();  
  29.         if (extras !=  null ) {  
  30.             mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,  
  31.                     AppWidgetManager.INVALID_APPWIDGET_ID);  
  32.         }  
  33.   
  34.         // If they gave us an intent without the widget id, just bail.  
  35.         if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {  
  36.             finish();  
  37.         }  
  38.   
  39.         mImBtn1 = (ImageButton) findViewById(R.id.ImageButton01);  
  40.         mImBtn2 = (ImageButton) findViewById(R.id.ImageButton02);  
  41.         mImBtn3 = (ImageButton) findViewById(R.id.ImageButton03);  
  42.         mImBtn4 = (ImageButton) findViewById(R.id.ImageButton04);  
  43.   
  44.         mImBtn1.setOnClickListener(mBtnClick);  
  45.         mImBtn2.setOnClickListener(mBtnClick);  
  46.         mImBtn3.setOnClickListener(mBtnClick);  
  47.         mImBtn4.setOnClickListener(mBtnClick);  
  48.     }  
  49.   
  50.     OnClickListener mBtnClick = new OnClickListener() {  
  51.         @Override  
  52.         public  void  onClick(View v) {  
  53.             int srcId = R.drawable.sketchy_paper_008;  
  54.             switch (v.getId()) {  
  55.             case R.id.ImageButton01:  
  56.                 srcId = R.drawable.sketchy_paper_003;  
  57.                 break;  
  58.             case R.id.ImageButton02:  
  59.                 srcId = R.drawable.sketchy_paper_004;  
  60.                 break;  
  61.             case R.id.ImageButton03:  
  62.                 srcId = R.drawable.sketchy_paper_007;  
  63.                 break;  
  64.             case R.id.ImageButton04:  
  65.                 srcId = R.drawable.sketchy_paper_011;  
  66.                 break;  
  67.             }  
  68.             Log.i("myLog" "mAppWidgetId is: "  + mAppWidgetId);  
  69.   
  70.             RemoteViews views = new RemoteViews(MyNoteConf. this   
  71.                     .getPackageName(), R.layout.my_note_widget);  
  72.             views.setImageViewResource(R.id.my_widget_img, srcId);  
  73.   
  74.             AppWidgetManager appWidgetManager = AppWidgetManager  
  75.                     .getInstance(MyNoteConf.this);  
  76.             appWidgetManager.updateAppWidget(mAppWidgetId, views);  
  77.   
  78.             // return OK  
  79.             Intent resultValue = new Intent();  
  80.             resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,  
  81.                     mAppWidgetId);  
  82.   
  83.             setResult(RESULT_OK, resultValue);  
  84.             finish();  
  85.         }  
  86.     };  
  87. }  

这时我们可以运行一下看看效果了,添加我们开发的MyNote的widget后,

会停止在配置界面等待用户输入便签内容,并选择桌面图标。用户选择后会在桌面上添加相应图标了!

c.实现数据存储

虽然用户可以指定桌面图标了,但是用户的便签内容还没有存储起来,需用把用户的便签内容持久化。

要把数据持久化,在android中最简单的办法是使用 SharedPreferences,更好的做法是使用SQLite,

更更好的做法是使用ContentProvider包装。

本实例侧重于讲解widget开发,因此偷个懒,使用最简单的方法 SharedPreferences 实现。

SharedPreferences 使用非常简单,首先用一个特定的 Prefix 获取我们自用的 Preferences 空间。

这个特定的 Prefix 一般使用当前类的全限定名,以免和其他程序冲突。

获取到的Preferences 空间可以想象为一个哈希表,可以使用putXXXX(key,content)系列方法向其中放入名值对。

因此我们要做的事情就非常简单了,首先指定一个特定前缀:  final String mPerfName = "com.silenceburn.MyNoteConf";

然后获取EditText的内容,获得一个Preferences引用,使用putString将EditText的内容放入Preferences,代码加下:

 

view plain copy to clipboard print ?
  1. TextView mTextView = (TextView) MyNoteConf.this   
  2.         .findViewById(R.id.EditText02);  
  3. SharedPreferences.Editor prefs = MyNoteConf.this  
  4.         .getSharedPreferences(mPerfName, 0).edit();  
  5. prefs.putString("DAT" + mAppWidgetId, mTextView.getText()  
  6.         .toString());  
  7. prefs.commit();  

注意这里putString时使用的Key是  "DAT" + mAppWidgetId ,

由于mAppWidgetId 是每个widget的唯一标示,这样就可以有效的区分不同的widget的内容进行分别存储了。

将上述代码加入onClick 即完成了配置Activity部分的编写。


6.增加 widget 点击响应,实现修改便签功能

OK,终于来到了最后一步,就要大功告成了。这一步中的分阶段目标有:

a. 增加widget点击响应

b. 实现修改便签的Activity界面

以下分阶段说明

a. 增加widget点击响应

首先我们新建一个Activity类,起名为 MyNoteEdit,widget被点击时,将调用该Activity。

由于是对便签内容进行修改,所以我们这里可以取巧偷个懒,布局直接复用 my_note_conf.xml。

在本阶段,重点问题是可以点击widget调用MyNoteEdit,因此不需要进行更多处理,所以代码很简单:

 

view plain copy to clipboard print ?
  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5.   
  6. public  class  MyNoteEdit  extends  Activity {  
  7.     @Override  
  8.     protected  void  onCreate(Bundle savedInstanceState) {  
  9.         // TODO Auto-generated method stub  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.my_note_conf);  
  12.     }  
  13. }  

之后,为了给widget增加点击响应,我们要再次修改一下第5小节中配置Activity的代码,

为widget附着上一个pendingIntent,这样当widget被点击时,就可以触发我们指定的Intent了。

代码片段如下:

 

view plain copy to clipboard print ?
  1. Intent intent = new  Intent(MyNoteConf. this , MyNoteEdit. class );  
  2. intent.setAction(mPerfName + mAppWidgetId);  
  3. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,mAppWidgetId);  
  4. PendingIntent pendingIntent = PendingIntent.getActivity(MyNoteConf.this 0 ,  
  5.         intent, 0);  
  6. views.setOnClickPendingIntent(R.id.my_widget_img, pendingIntent);  

注意这里我们使用intent.setAction(mPerfName + mAppWidgetId);为每个widget赋予了独一无二的Action。

否则获得的pendingIntent实际是同一个实例,仅extraData不同,根据创建pendingIntent方法的不同,

extraData可能会被覆盖或者只初始化一次不再改变(getActivity的最后一个参数flags决定)。

这样我们在pendingIntent中就只能得到第一个新增的widget的Id,或者最后一次新增的widget的Id,

这显然不是我们希望看到的。

最后千万不要忘记在AndroidManifest.xml中添加我们新增的MyNoteEdit的声明,  <activity android:name=".MyNoteEdit"/>

至此点击响应增加完成,可以运行一下看看效果,生成widget到桌面后,可以点击widget激活修改窗口。

修改窗口复用了my_note_conf.xml 作为layout,所以和配置Activity看起来是一摸一样的。 

 

b. 实现修改便签的Activity界面

到目前为止,绝大多数功能已经实现完毕,就差最后一小步了,就是修改便签内容。

因为在第五节的c小节中,我们putString时使用的Key是  "DAT" + mAppWidgetId ,

所以我们在MyNoteEdit的onCreate里面,获取激发了此Activity的Intent,

从Intent中取出放在extraData里的widgetId,就可以用此ID从perference中取出便签内容,并setText到EditText控件中。

这部分代码片段如下:

 

view plain copy to clipboard print ?
  1. Intent t = getIntent();  
  2. Log.i("myLog",t.getAction());  
  3. mAppWidgetId = t.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,-1);  
  4. Log.i("myLog", "it's ["  + mAppWidgetId +  "] editing!" );  
  5.   
  6. mPref = getSharedPreferences(mPerfName, 0);  
  7. String noteContent = mPref.getString("DAT"+ mAppWidgetId,  "" );  
  8.   
  9. TextView mTextView= (TextView) findViewById(R.id.EditText02);  
  10. mTextView.setText(noteContent);   

而对于四个imageButton的点击事件,则几乎可以完全照抄MyNoteConf的实现。去掉一些无关代码即可。

最终完成的MyNoteEdit的代码如下:

 

view plain copy to clipboard print ?
  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.appwidget.AppWidgetManager;  
  5. import android.content.Intent;  
  6. import android.content.SharedPreferences;  
  7. import android.os.Bundle;  
  8. import android.util.Log;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.widget.ImageButton;  
  12. import android.widget.RemoteViews;  
  13. import android.widget.TextView;  
  14.   
  15. public  class  MyNoteEdit  extends  Activity {  
  16.     int mAppWidgetId;  
  17.     TextView mTextView;  
  18.     ImageButton mImBtn1, mImBtn2, mImBtn3, mImBtn4;  
  19.   
  20.     final String mPerfName =  "com.silenceburn.MyNoteConf" ;  
  21.     SharedPreferences mPref;  
  22.   
  23.     @Override  
  24.     protected  void  onCreate(Bundle savedInstanceState) {  
  25.         // TODO Auto-generated method stub  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.my_note_conf);  
  28.   
  29.         Intent t = getIntent();  
  30.         Log.i("myLog", t.getAction());  
  31.         mAppWidgetId = t.getExtras().getInt(  
  32.                 AppWidgetManager.EXTRA_APPWIDGET_ID, -1);  
  33.         Log.i("myLog" "it's ["  + mAppWidgetId +  "] editing!" );  
  34.   
  35.         mPref = getSharedPreferences(mPerfName, 0);  
  36.         String noteContent = mPref.getString("DAT" + mAppWidgetId,  "" );  
  37.   
  38.         mTextView = (TextView) findViewById(R.id.EditText02);  
  39.         mTextView.setText(noteContent);  
  40.         mImBtn1 = (ImageButton) findViewById(R.id.ImageButton01);  
  41.         mImBtn2 = (ImageButton) findViewById(R.id.ImageButton02);  
  42.         mImBtn3 = (ImageButton) findViewById(R.id.ImageButton03);  
  43.         mImBtn4 = (ImageButton) findViewById(R.id.ImageButton04);  
  44.   
  45.         mImBtn1.setOnClickListener(mBtnClick);  
  46.         mImBtn2.setOnClickListener(mBtnClick);  
  47.         mImBtn3.setOnClickListener(mBtnClick);  
  48.         mImBtn4.setOnClickListener(mBtnClick);  
  49.   
  50.     }  
  51.   
  52.     OnClickListener mBtnClick = new OnClickListener() {  
  53.   
  54.         @Override  
  55.         public  void  onClick(View v) {  
  56.   
  57.             SharedPreferences.Editor prefsEdit = mPref.edit();  
  58.             prefsEdit.putString("DAT" + mAppWidgetId, mTextView.getText()  
  59.                     .toString());  
  60.             prefsEdit.commit();  
  61.   
  62.             int srcId = R.drawable.sketchy_paper_008;  
  63.             switch (v.getId()) {  
  64.             case R.id.ImageButton01:  
  65.                 srcId = R.drawable.sketchy_paper_003;  
  66.                 break;  
  67.             case R.id.ImageButton02:  
  68.                 srcId = R.drawable.sketchy_paper_004;  
  69.                 break;  
  70.             case R.id.ImageButton03:  
  71.                 srcId = R.drawable.sketchy_paper_007;  
  72.                 break;  
  73.             case R.id.ImageButton04:  
  74.                 srcId = R.drawable.sketchy_paper_011;  
  75.                 break;  
  76.             }  
  77.   
  78.             RemoteViews views = new RemoteViews(MyNoteEdit. this   
  79.                     .getPackageName(), R.layout.my_note_widget);  
  80.             views.setImageViewResource(R.id.my_widget_img, srcId);  
  81.   
  82.             AppWidgetManager appWidgetManager = AppWidgetManager  
  83.                     .getInstance(MyNoteEdit.this);  
  84.             appWidgetManager.updateAppWidget(mAppWidgetId, views);  
  85.   
  86.             MyNoteEdit.this.finish();  
  87.         }  
  88.     };  
  89. }  

至此修改功能也已经完成,本程序的所有功能添加完毕。我们可以如文初所述的那样,

可以通过桌面增加我们的MyNote小部件,输入便签内容,指定图标,

点击桌面上的便签,可以再次对便签内容进行修改,并更换图标。

桌面上可以同时存在多个便签,并可以分别进行修改


7.总结

本文通过介绍android便签软件的开发过程,讲解了android widget开发的一般方法和过程。

在开发过程中,我故意强调了分阶段渐进式的开发方法,力求每阶段都有可运行的可交付产品,

某种程度上实践了敏捷开发的理念。

不过由于本文只是个示例程序,因此对程序的美化、优化都很不足,在编程规范上也有散漫的地方。

这都是本程序的不足之处。

审视整个程序,会发现大量代码集中于配置界面和修改界面,

反而在appWidgetProvider中只有记录日志、清理Perferences等简单工作。

这虽然与本程序的需求目的有关,但是也跟为了省电android要求尽量降低自动update频率有关。(推荐值是至多一个小时一次)

所以我们尽量只在需要时,才用RemoteViews和appWidgetManager.updateAppWidget方法显式的去要求widget更新。

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 别人登我的淘宝怎么办 植物2被禁止登录怎么办 淘宝网东西未收到怎么办 淘宝网卖家不许退货退款怎么办 身份证以前开过淘宝店怎么办 支付宝登录名尚未激活怎么办 淘宝退货卖家不收货退款买家怎么办 淘宝账号刷得太多违规怎么办 闲鱼交易关闭了怎么办 淘宝店开了没做怎么办 微店店铺严重违规怎么办 淘宝违规扣2分怎么办 淘宝被扣6分怎么办 淘宝被扣2分怎么办 淘宝被海关扣了怎么办 淘宝被扣36分后怎么办 淘宝售假查封店铺资金怎么办 淘宝店扣48分怎么办 淘宝a内被扣48分怎么办 饿了么店铺满减怎么办 淘宝店扣a48分怎么办 淘宝短信营销无法获取人群怎么办 淘宝货发了退款怎么办 极速退款后卖家不确认收货怎么办 把货退了卖家不退款怎么办? 退款了又收到货怎么办 退货忘了填单号怎么办 手机换号了淘宝怎么办 换了手机支付宝怎么办 手机丢了微信登不上去了怎么办 前面手机丢了微信登不上去怎么办 淘宝密码忘了怎么办呢 融e借逾期一天怎么办 拼多多处罚下架怎么办 永久无法解绑支付宝怎么办 淘宝下单购买人数太多怎么办 新浪微博被拉黑暂时无法评论怎么办 闲鱼交易成功后卖家反悔怎么办 闲鱼买家不申请介入怎么办 支付宝安装不上怎么办 无线摄像机离wifi太远怎么办