WIDGET是安卓桌面的控件,使用方便直观。That's cool,isn't it?
WIDGET的本质是一个BroadcastReceiver,它有自己的生命周期。生命周期的方法如下:
1.onEnabled方法:此方法在Widget第一次被创建的时候调用,并且只调用一次,此方法中常放入初始化数据,服务的操作。
2.onReceive方法:通BroadcastReceiver的OnReceive方法,但是这里有所不同的是,当接收到Widget操作时首先调用的是OnReceive方法,然后才是相关的操作方法。这也
很好理解,Widget的是运行在桌面运用程序中的小控件,当自己的应用程序需要调用Widget是,就需要发送广播事件去调用。
3.onUpdate:Widget在固定的时间里更新时调用的方法。
4.onDeleted:Widget被删除时调用的方法。
5.onDisabled:所用Widget被删除是调用的方法,同onEnabled方法相对。
本例设计了一个实时天气的WIDGET。WIDGET的显示板上显示了当天的所选城市的天气。点击显示板,可以查看所选城市的详细天气并允许修改城市。程序还会根据天气更换不同的手机壁纸。
AndroidMainfest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.memeda.lsy.widgettest"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme"> <receiver android:label="Hello,App Widget" android:name=".HelloWidgetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="com.demo.appwidget.refresh"></action> </intent-filter> <meta-data android:resource="@xml/provider_info" android:name="android.appwidget.provider"></meta-data> </receiver> <activity android:name=".SetWeatherUI"/> <service android:name=".Weather_Service"/> </application> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.SET_WALLPAPER"/> </manifest>
应用申请了壁纸更改的权限用来更改壁纸,还申请了网络权限用以获取天气信息。
值得注意的是,WIDGET桌面小控件的入口不再是一个activity,而变成了一个receiver。
provider_info.xml
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="225dip" android:minHeight="70dip" android:initialLayout="@layout/main"/>
设置桌面组件的长宽及初始layout文件。此例面板为一格高,三格宽。
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <AnalogClock android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/analogClock" android:layout_weight="2"/> <TextView android:id="@+id/textview" android:gravity="left" android:layout_width="match_parent" android:layout_height="match_parent" android:text="请点击设置城市" android:textSize="20sp" android:layout_weight="1" /> </LinearLayout>
设置了WIDGET的面板布局,该布局由一个时钟和一个TextView水平组成。
HelloWidgetProvider.java
package com.memeda.lsy.widgettest; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; import android.text.format.Time; import android.widget.RemoteViews; import android.widget.Toast; /** * Created by Administrator on 2015/2/9. */ public class HelloWidgetProvider extends AppWidgetProvider { static public Context myContext; @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { myContext.startService(new Intent(myContext,Weather_Service.class)); Toast.makeText(context,"请设置城市",Toast.LENGTH_SHORT).show(); RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.main); //设置点击事件,打开Activity Intent intent = new Intent(context,SetWeatherUI.class); PendingIntent pendingIntent = PendingIntent.getActivity(context,0,intent,0); remoteViews.setOnClickPendingIntent(R.id.textview,pendingIntent); appWidgetManager.updateAppWidget(appWidgetIds[0],remoteViews); } @Override public void onEnabled(Context context) { super.onEnabled(context); this.myContext = context; } }
设置WIDGET的初始状态。因为随着时间变长,onUpdate是会失去响应的,所以只能算初始状态(至于为什么我也不知道,忘大大指点一下)。将main.xml中的textview显示面板设为可点击的,其点击事件即是打开SetWeatherUI.class这个Activity。
接下来我们来看看SetWeatherUI这个Activity。
set_city_ui.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="8" android:id="@+id/cityInput" android:hint="请输入您的城市"/> <Button android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:id="@+id/cityButton" android:layout_gravity="center" android:textSize="20sp" android:text="获取天气"/> </LinearLayout> <TextView android:id="@+id/cityWeather" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="left" android:minLines="5" android:textSize="20sp" /> </LinearLayout>
以上是SetWeatherUI的布局文件主要包含了一个EditText用来输入城市,一个Button用来确认,一个TextView用来显示所选城市的天气详细信息。
SetWeatherUI.java
package com.memeda.lsy.widgettest; import android.app.Activity; import android.app.PendingIntent; import android.app.ProgressDialog; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.RemoteViews; import android.widget.TextView; import com.memeda.lsy.widgettest.net.UrlGetWeatherUtil; import java.util.Map; /** * Created by Administrator on 2015/2/10. */ public class SetWeatherUI extends Activity { EditText cityInput; TextView cityWeather; Button cityButton; String stringCityName; String weatherString,weatherDetails; ProgressDialog dialog2; ActivityReceive activityReceive; SharedPreferences sharedPreferences; SharedPreferences.Editor editor; public final static String WEATHER_SER = "com.memeda.lsy.action.WEATHER_SER"; public final static String WEATHER_ACTI= "com.memeda.lsy.action.WEATHER_ACTI"; public class ActivityReceive extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String state = intent.getStringExtra("state"); switch (state){ case "Loading": dialog2 = ProgressDialog.show(context , "提示", "加载中……"); //dialog2.show(); break; case "finishLoading": dialog2.cancel(); weatherDetails = intent.getStringExtra("weatherDetail"); cityWeather.setText(weatherDetails); weatherString=intent.getStringExtra("weatherString"); RemoteViews appWidgetView = new RemoteViews(SetWeatherUI.this.getPackageName(),R.layout.main); appWidgetView.setTextViewText(R.id.textview,weatherString); AppWidgetManager.getInstance(SetWeatherUI.this) .updateAppWidget(new ComponentName(SetWeatherUI.this, HelloWidgetProvider.class), appWidgetView); break; default: cityWeather.setText("未响应"); break; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.set_city_ui); activityReceive = new ActivityReceive(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(WEATHER_ACTI); registerReceiver(activityReceive,intentFilter); Intent intent =new Intent(SetWeatherUI.this,Weather_Service.class); startService(intent); sharedPreferences=getSharedPreferences("weatherInfo",MODE_WORLD_READABLE); editor=sharedPreferences.edit(); stringCityName=sharedPreferences.getString("cityName","北京"); Intent intentInit = new Intent(WEATHER_SER); intentInit.putExtra("command","changeCity"); intentInit.putExtra("cityName",stringCityName); sendBroadcast(intentInit); cityInput= (EditText) findViewById(R.id.cityInput); cityInput.setText(stringCityName); cityWeather= (TextView) findViewById(R.id.cityWeather); cityButton = (Button) findViewById(R.id.cityButton); cityButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stringCityName=cityInput.getText().toString(); Intent intent = new Intent(WEATHER_SER); intent.putExtra("command","changeCity"); intent.putExtra("cityName",stringCityName); sendBroadcast(intent); editor.putString("cityName",stringCityName); editor.commit(); } }); } }
SetWeatherUI只做了4件事:1、更新Activity界面;2、更新WidGet;3、向Service发请求获取天气信息;4、保存用户设定的城市信息。
1&2)更新Activity界面&更新WidGet:
public void onReceive(Context context, Intent intent) { String state = intent.getStringExtra("state"); switch (state){ case "Loading": dialog2 = ProgressDialog.show(context , "提示", "加载中……"); //dialog2.show(); break; case "finishLoading": dialog2.cancel(); weatherDetails = intent.getStringExtra("weatherDetail"); cityWeather.setText(weatherDetails); weatherString=intent.getStringExtra("weatherString"); RemoteViews appWidgetView = new RemoteViews(SetWeatherUI.this.getPackageName(),R.layout.main); appWidgetView.setTextViewText(R.id.textview,weatherString); AppWidgetManager.getInstance(SetWeatherUI.this) .updateAppWidget(new ComponentName(SetWeatherUI.this, HelloWidgetProvider.class), appWidgetView); break; default: cityWeather.setText("未响应"); break; } }
SetWeatherUI根据Service发回来的广播,更改UI,并更改WidGet显示信息。SetWeatherUI显示weatherDetail,WidGet显示weatherString。
3&4)向Service发请求获取天气信息&保存用户设定的城市信息。
cityButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stringCityName=cityInput.getText().toString(); //向Weather_Service发送广播要求更改城市 Intent intent = new Intent(WEATHER_SER); intent.putExtra("command","changeCity"); intent.putExtra("cityName",stringCityName); sendBroadcast(intent); editor.putString("cityName",stringCityName); editor.commit(); } });
当确认键按下后,运用广播向Weather_Service发送更改城市的命令,并将城市名称传入。随后使用SharedPreferences保存城市名称。
接下来是Weather_Service的设计
package com.memeda.lsy.widgettest; import android.app.PendingIntent; import android.app.ProgressDialog; import android.app.Service; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; import com.memeda.lsy.widgettest.net.UrlGetWeatherUtil; import java.io.IOException; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * Created by Administrator on 2015/2/10. */ public class Weather_Service extends Service { MyReceiver myReceiver; SharedPreferences sharedPreferences; SharedPreferences.Editor editor; Timer timer; WallpaperManager wallpaperManager; int wallPaperCount=0; int[] snowWallPaperIds=new int[]{R.drawable.background_snow1,R.drawable.background_snow2}; int[] sunWallPaperIds=new int[]{R.drawable.background_sunday1,R.drawable.background_sunday2}; int[] cloudWallPaperIds=new int[]{R.drawable.background_cloud1,R.drawable.background_cloud2}; int[] rainWallPaperIds=new int[]{R.drawable.background_rain1,R.drawable.background_rain2}; int[] darkWallPaperIds=new int[]{R.drawable.background_dark1,R.drawable.background_dark2}; class ShowWeatherTask extends AsyncTask<String,Integer,Map<String,String>> { Context myContext; public ShowWeatherTask(Context context){ myContext=context; } @Override protected Map<String,String> doInBackground(String... params) { //通过Url的GET请求在百度API上获得天气信息并使用JSON解析后储存 Map<String,String> map = UrlGetWeatherUtil.getWeather(params[0]); editor.putString("weather",map.get("weather")); editor.commit(); getWallPaper(map.get("weather")); return map; } @Override protected void onPreExecute() { Intent intent = new Intent(SetWeatherUI.WEATHER_ACTI); intent.putExtra("state","Loading"); sendBroadcast(intent); super.onPreExecute(); } @Override protected void onPostExecute(Map<String,String> s) { super.onPostExecute(s); String weatherString=""; String weatherDetail=""; Intent intent = new Intent(SetWeatherUI.WEATHER_ACTI); //更新状态:天气信息获取完成 intent.putExtra("state","finishLoading"); if(s!=null){ weatherString=showEasyInfo(s); weatherDetail=showDetailInfo(s); } else { weatherString="未能成功获取天气信息"; weatherDetail="未能成功获取天气信息"; } //更新WidGet的界面 upDataWidget(weatherString); //将天气信息发送给UI界面 intent.putExtra("weatherDetail", weatherDetail); intent.putExtra("weatherString",weatherString); sendBroadcast(intent); } } public class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String command = intent.getStringExtra("command"); switch (command){ case "changeCity":{ //获得城市名称 String cityName = intent.getStringExtra("cityName"); //获取天气信息并提交 ShowWeatherTask showWeatherTask = new ShowWeatherTask(context); showWeatherTask.execute(cityName); //保存城市信息 editor.putString("cityName",cityName); editor.commit(); break; } case "updateWeather":{ //提取保存的城市信息 String cityName=sharedPreferences.getString("cityName","北京"); //获取天气信息并提交 ShowWeatherTask showWeatherTask = new ShowWeatherTask(context); showWeatherTask.execute(cityName); break; } } } } @Override public void onCreate() { super.onCreate(); //注册广播接收器 myReceiver = new MyReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(SetWeatherUI.WEATHER_SER); registerReceiver(myReceiver, intentFilter); //获取SharedPreferences sharedPreferences = getSharedPreferences("weatherInfo", MODE_WORLD_READABLE); editor = sharedPreferences.edit(); //获取WallpapaerManager以更改墙纸 wallpaperManager = WallpaperManager.getInstance(this); //定时更新天气,更换壁纸 timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { handler.sendEmptyMessage(0x123); } },0,30000); } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what==0x123){ //获取城市信息 String cityName=sharedPreferences.getString("cityName","北京"); //通过网络获取天气信息 ShowWeatherTask showWeatherTask = new ShowWeatherTask(Weather_Service.this); showWeatherTask.execute(cityName); } super.handleMessage(msg); } }; //更换壁纸 private void changeWallPaper(int[] weatherPicture ){ wallPaperCount=wallPaperCount%weatherPicture.length; try { wallpaperManager.setResource(weatherPicture[wallPaperCount]); wallPaperCount++; } catch (IOException e) { e.printStackTrace(); } } //更新WidGet的界面 private void upDataWidget(String detail){ RemoteViews remoteViews = new RemoteViews(Weather_Service.this.getPackageName(),R.layout.main); //设置点击事件,打开Activity Intent intent = new Intent(Weather_Service.this,SetWeatherUI.class); PendingIntent pendingIntent = PendingIntent.getActivity(Weather_Service.this,0,intent,0); remoteViews.setOnClickPendingIntent(R.id.textview,pendingIntent); remoteViews.setTextViewText(R.id.textview,detail); AppWidgetManager.getInstance(Weather_Service.this) .updateAppWidget(new ComponentName(Weather_Service.this, HelloWidgetProvider.class), remoteViews); } //根据天气获得相应壁纸 private void getWallPaper(String tempWeather){ if(tempWeather.equals("晴")){ changeWallPaper(sunWallPaperIds); } else if(tempWeather.contains("雪")){ changeWallPaper(snowWallPaperIds); } else if(tempWeather.contains("雨")){ changeWallPaper(rainWallPaperIds); } else if(tempWeather.contains("云")){ changeWallPaper(cloudWallPaperIds); } else if(tempWeather.equals("阴")){ changeWallPaper(darkWallPaperIds); } } @Override public IBinder onBind(Intent intent) { return null; } //把将要显示在Widget上的信息转化成字符串 public String showEasyInfo(Map<String,String> weather){ String weatherString=new String(); weatherString=""+weather.get("city"); weatherString+=" "+weather.get("weather"); weatherString+=" "+weather.get("l_tmp")+"~"+weather.get("h_tmp"); weatherString+=" "+weather.get("WS"); return weatherString; } //把将要显示在Activity上的信息转化成字符串 public String showDetailInfo(Map<String,String> weather){ String weatherString=new String(); weatherString="城市:"+weather.get("city"); weatherString+=" 天气:"+weather.get("weather"); weatherString+=" 当前气温:"+weather.get("temp"); weatherString+=" 今日气温:"+weather.get("l_tmp")+"~"+weather.get("h_tmp"); weatherString+=" 发布时间:"+weather.get("time"); weatherString+=" 邮编:"+weather.get("postCode"); weatherString+=" 经度:"+weather.get("longitude"); weatherString+=" 纬度:"+weather.get("latitude"); weatherString+=" 风向:"+weather.get("WD"); weatherString+=" 风力:"+weather.get("WS"); weatherString+=" 日出时间:"+weather.get("sunrise"); weatherString+=" 日落时间:"+weather.get("sunset"); return weatherString; } }
Weather_Service只做两件事:1、接收广播并给出回应;2、定时更新Widget的显示信息并更换墙纸。
1)接收广播并给出回应
class ShowWeatherTask extends AsyncTask<String,Integer,Map<String,String>> { Context myContext; public ShowWeatherTask(Context context){ myContext=context; } @Override protected Map<String,String> doInBackground(String... params) { //通过Url的GET请求在百度API上获得天气信息并使用JSON解析后储存 Map<String,String> map = UrlGetWeatherUtil.getWeather(params[0]); editor.putString("weather",map.get("weather")); editor.commit(); getWallPaper(map.get("weather")); return map; } @Override protected void onPreExecute() { Intent intent = new Intent(SetWeatherUI.WEATHER_ACTI); intent.putExtra("state","Loading"); sendBroadcast(intent); super.onPreExecute(); } @Override protected void onPostExecute(Map<String,String> s) { super.onPostExecute(s); String weatherString=""; String weatherDetail=""; Intent intent = new Intent(SetWeatherUI.WEATHER_ACTI); //更新状态:天气信息获取完成 intent.putExtra("state","finishLoading"); if(s!=null){ weatherString=showEasyInfo(s); weatherDetail=showDetailInfo(s); } else { weatherString="未能成功获取天气信息"; weatherDetail="未能成功获取天气信息"; } //更新WidGet的界面 upDataWidget(weatherString); //将天气信息发送给UI界面 intent.putExtra("weatherDetail", weatherDetail); intent.putExtra("weatherString",weatherString); sendBroadcast(intent); } } public class MyReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { String command = intent.getStringExtra("command"); switch (command){ case "changeCity":{ //获得城市名称 String cityName = intent.getStringExtra("cityName"); //获取天气信息并提交 ShowWeatherTask showWeatherTask = new ShowWeatherTask(context); showWeatherTask.execute(cityName); //保存城市信息 editor.putString("cityName",cityName); editor.commit(); break; } case "updateWeather":{ //提取保存的城市信息 String cityName=sharedPreferences.getString("cityName","北京"); //获取天气信息并提交 ShowWeatherTask showWeatherTask = new ShowWeatherTask(context); showWeatherTask.execute(cityName); break; } } } }
获取天气信息的过程通过AsyncTask异步任务类进行管理,使用Url的Get请求方法,百度API的数据,通过JSON解析,获得天气信息。
2)定时更新Widget显示信息
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what==0x123){ //获取城市信息 String cityName=sharedPreferences.getString("cityName","北京"); //通过网络获取天气信息 ShowWeatherTask showWeatherTask = new ShowWeatherTask(Weather_Service.this); showWeatherTask.execute(cityName); } super.handleMessage(msg); } };
以上