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+="\n"+weather.get("weather");
        weatherString+="\n"+weather.get("l_tmp")+"~"+weather.get("h_tmp");
        weatherString+="\n"+weather.get("WS");
        return weatherString;
    }
    //把将要显示在Activity上的信息转化成字符串
    public String showDetailInfo(Map<String,String> weather){
        String weatherString=new String();
        weatherString="城市:"+weather.get("city");
        weatherString+="\n天气:"+weather.get("weather");
        weatherString+="\n当前气温:"+weather.get("temp");
        weatherString+="\n今日气温:"+weather.get("l_tmp")+"~"+weather.get("h_tmp");
        weatherString+="\n发布时间:"+weather.get("time");
        weatherString+="\n邮编:"+weather.get("postCode");
        weatherString+="\n经度:"+weather.get("longitude");
        weatherString+="\n纬度:"+weather.get("latitude");
        weatherString+="\n风向:"+weather.get("WD");
        weatherString+="\n风力:"+weather.get("WS");
        weatherString+="\n日出时间:"+weather.get("sunrise");
        weatherString+="\n日落时间:"+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);
        }
    };

以上

 

posted on 2015-02-18 14:37  Fishbonell  阅读(221)  评论(0编辑  收藏  举报