本篇文章将分享两个VIEW组件,一个天气组件和一个日期组件,这两个组件本来是一个App Widget 后来,我看着好玩,将他们弄成一个VIEW的组件,可以像使用Windows Phone 7 的用户控件一样拖放到你想要的项目中。本篇将演示这两个组件的编写过程,工程文件如下:
包名介绍:
com.terry.weather 程序的入口包com.yaomei.adapter 天气预报组件使用到的数据源com.yaomei.model 天气预报使用到的模型包com.yaomei.util 获取天气信息的工具包com.yaomei.widget 天气预报组件、日期组件的存放位置从包名可以看出,编写一个天气预报所需要的代码量比编写一个日期VIEW所需要的代码量要多得多 ,那么我们先把天气预报的一些实现思路跟大家讲讲。
首先,本实例使用的天气预报是一个可以自己国际化的天气组件VIEW,可以看上图,将所需要的URL都放入ANDROID 自己的国际化文件夹里面,比如中文的话就这样写:
< string name ="googleWeatherApi" > <![CDATA[ http://www.google.com/ig/api?hl=zh-cn&weather= ]]> </ string >
那么是英语环境的就只需要在默认的VALUES里面的string.xml这样写即可:
< string name ="googleWeatherApi" > <![CDATA[ http://www.google.com/ig/api?hl=en&weather= ]]> </ string >
这是本篇一个要注意的一点,另外还有需要注意的是,这个天气组件提供可供用户选择更新频率,这里比如我们使用3个小时更新一次,那么当用户退出程序时,再打开是否还要再去Google 上面读天气呢?答案是NO,因为既然用户选择了更新频率,那么在一定的时间内,我们最好不要自动去更新,除非用户自己点击更新才去执行。那么要如何得到之前的数据呢?
这里使用到的是SharePreference 将一些天气的信息保存进去,连同天气的图片也一并保存。保存天气图片是将google 天气的图片使用Base64转成字符串,然后保存进Sharepreference ,如果更新频率条件未满足则进去SharePrference 将天气预报数据取出来 。因为Android 并未提供将图片转成字符串的API,这里使用到的是apache 的一个Jar包,可在这里下载:点击这里
思路上面给出了,下面给出天气预报组件VIEW的核心代码,其他附属代码可在后面的附件下载得到,代码如下:
package com.yaomei.widget; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.codec.binary.Base64; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Bitmap.CompressFormat; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.text.Html; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.terry.weather.R; import com.yaomei.adapter.weatherAdapter; import com.yaomei.model.WeatherMdoel; import com.yaomei.util.strHelpeUtil; public class WeatherView extends LinearLayout { private static final String Hour_COMPARE = " hour_compare " ; private static final String DAY_OF_WEEK = " day_of_week " ; private static final String LOW = " low " ; private static final String HIGH = " high " ; private static final String CONDITION = " condition " ; private static final String IMAGE = " image " ; private static final String DATE_COMPARE = " date_compare " ; private static final String CITYNAE_SHARE = " cityNameShare " ; private ImageView iv_weather; private TextView tv_state, tv_position, tv; WeatherMdoel model; private List < WeatherMdoel > weatherList = null ; GridView gv; Timer timer; Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.arg1 == 1 ) { if (weatherList.size() > 0 ) { gv .setAdapter( new weatherAdapter(getContext(), weatherList)); init(); } else { Toast.makeText(getContext(), " 查询不到数据 " , 1000 ).show(); } // msg.recycle(); } }; }; /** * 自动加载天气 */ private boolean autoLoad = false ; public boolean getAutoLoad() { return autoLoad; } public void setAutoLoad( boolean isLoad) { this .autoLoad = isLoad; } /** * 城市名称 */ private String cityName = "" ; public String getCityName() { return cityName; } public void setCityName(String cityName) { this .cityName = cityName; } /** * 设置每几小时更新一次 */ private int updateHour; public int getUpdateHour() { return updateHour; } public void setUpdateHour( int hour) { this .updateHour = hour; } public WeatherView(Context context) { this (context, null ); // TODO Auto-generated constructor stub } public WeatherView(Context context, AttributeSet attrs) { super (context, attrs); int resouceID = - 1 ; TypedArray tyedArray = context.obtainStyledAttributes(attrs, R.styleable.WeatherView); int N = tyedArray.getIndexCount(); for ( int i = 0 ; i < N; i ++ ) { int attr = tyedArray.getIndex(i); switch (attr) { case R.styleable.WeatherView_AutoLoad: setAutoLoad(tyedArray.getBoolean( R.styleable.WeatherView_AutoLoad, false )); break ; case R.styleable.WeatherView_CityName: resouceID = tyedArray.getResourceId( R.styleable.WeatherView_CityName, 0 ); setCityName(resouceID > 0 ? tyedArray.getResources().getText( resouceID).toString() : tyedArray .getString(R.styleable.WeatherView_CityName)); break ; case R.styleable.WeatherView_UpdateHour: setUpdateHour(tyedArray.getInteger( R.styleable.WeatherView_UpdateHour, 3 )); break ; } } View view = LayoutInflater.from(getContext()).inflate( R.layout.weather_layout, this ); tv = (TextView) view.findViewById(R.id.tv_temperature); gv = (GridView) view.findViewById(R.id.grid); iv_weather = (ImageView) view.findViewById(R.id.iv_weather); tv_state = (TextView) view.findViewById(R.id.tv_state); tv_position = (TextView) view.findViewById(R.id.tv_position); timer = new Timer(); if (getAutoLoad()) { startLoadWeather(); } tyedArray.recycle(); } /** * 开始加载 */ public void startLoadWeather() { timer.schedule( new TimerTask() { @Override public void run() { SharedPreferences share = getContext().getSharedPreferences( " weather " , Activity.MODE_PRIVATE); long time = System.currentTimeMillis(); final Calendar mCalendar = Calendar.getInstance(); mCalendar.setTimeInMillis(time); String tempDate = mCalendar.get(Calendar.YEAR) + " - " + mCalendar.get(Calendar.MONTH) + " - " + mCalendar.get(Calendar.DAY_OF_MONTH); if (share.contains(DATE_COMPARE)) { if (share.getString(CITYNAE_SHARE, "" ).equals(cityName)) { int time_cop = mCalendar.get(Calendar.HOUR) - share.getInt(Hour_COMPARE, 0 ); String date = share.getString(DATE_COMPARE, "" ); if (time_cop >= getUpdateHour() || ! date.equals(tempDate)) { saveWeatherList(mCalendar.get(Calendar.HOUR), tempDate); } else if (time_cop < getUpdateHour()) { weatherList = new ArrayList < WeatherMdoel > (); for ( int i = 0 ; i < 4 ; i ++ ) { WeatherMdoel model = new WeatherMdoel(); model.setWeek(share.getString(DAY_OF_WEEK + i, "" )); model.setLowTemp(share.getString(LOW + i, "" )); model .setHighTemp(share.getString(HIGH + i, "" )); model.setConditions(share.getString(CONDITION + i, "" )); String image = share.getString(IMAGE + i, "" ); byte [] base64Bytes = Base64.decodeBase64(image .getBytes()); ByteArrayInputStream bais = new ByteArrayInputStream( base64Bytes); model.setImageUrl( "" ); model .setImageDrawable(Drawable .createFromStream(bais, " weather_image " )); weatherList.add(model); } } } else { saveWeatherList(mCalendar.get(Calendar.HOUR), tempDate); } } else { saveWeatherList(mCalendar.get(Calendar.HOUR), tempDate); } // 把必要的操作放在于线程中执行,不阻塞UI if (handler.hasMessages( 1 )) handler.obtainMessage().recycle(); else { Message msg = handler.obtainMessage(); msg.arg1 = 1 ; msg.sendToTarget(); } } }, 0 , getUpdateHour() * 3600 * 1000 ); } /** * 第一次或者另外重新加载 */ void saveWeatherList( int hour, String day) { weatherList = new ArrayList < WeatherMdoel > (); weatherList = strHelpeUtil.searchWeather(Html.fromHtml( getContext().getResources() .getString(R.string.googleWeatherApi)).toString(), getCityName()); SharedPreferences.Editor shareEditor = getContext() .getSharedPreferences( " weather " , Activity.MODE_PRIVATE).edit(); shareEditor.clear(); int i = 0 ; for (WeatherMdoel model : weatherList) { shareEditor.putString(DAY_OF_WEEK + i, model.getWeek()); shareEditor.putString(LOW + i, model.getLowTemp()); shareEditor.putString(HIGH + i, model.getHighTemp()); shareEditor.putString(CONDITION + i, model.getConditions()); /** * 将图片存入 */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ((BitmapDrawable) strHelpeUtil.loadImage(model.getImageUrl())) .getBitmap().compress(CompressFormat.JPEG, 50 , baos); String ImageBase64 = new String(Base64.encodeBase64(baos .toByteArray())); shareEditor.putString(IMAGE + i, ImageBase64); i ++ ; } shareEditor.putString(DATE_COMPARE, day); shareEditor.putInt(Hour_COMPARE, hour); shareEditor.putString(CITYNAE_SHARE, cityName); shareEditor.commit(); } /** * 初始化组件 信息 */ void init() { model = weatherList.get( 0 ); iv_weather.setImageDrawable(model.getImageUrl() == "" ? model .getImageDrawable() : strHelpeUtil.loadImage(model .getImageUrl())); tv_state.setText(model.getConditions()); tv_position.setText(getCityName()); tv.setText(getContext().getResources().getString(R.string.temp_format, model.getLowTemp(), model.getHighTemp())); } /** * 释放对象 */ public void releaseTimer() { timer.cancel(); weatherList = null ; }}
学习这个类,你能够学到的知识点为:为应用程序添加属性,编写组件,SharePreference 的使用,Timer和Handler 异步处理UI等知识点。
日期VIEW显示VIEW组件,是一个显示当前系统时间的组件,当第一次运行时,得到当前的秒数在以60秒减去当前秒,得到第一次运行时下一次运行需要的秒数,当这一次更新完毕后,下一次每次60秒更新一次时间,这个组件也是以分更新UI的操作,学习本类,你可以学到两个Handler 是如何协作处理UI,代码如下:
package com.yaomei.widget; import java.util.Calendar; import java.util.Date; import android.content.Context; import android.os.Handler; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; import com.terry.weather.R; import com.yaomei.util.strHelpeUtil; public class DateView extends FrameLayout { private TextView tv_date_time, tv_week, tv_date; int second; Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { init(); handler.sendMessageDelayed(handler.obtainMessage(), 60 * 1000 ); }; }; public DateView(Context context) { this (context, null ); } public DateView(Context context, AttributeSet attrs) { super (context, attrs); // this.setBackgroundDrawable(getContext().getResources().getDrawable( // R.drawable.date_background)); View view = LayoutInflater.from(getContext()).inflate( R.layout.date_layout, this ); tv_date_time = (TextView) view.findViewById(R.id.tv_date_time); tv_week = (TextView) view.findViewById(R.id.tv_week); tv_date = (TextView) view.findViewById(R.id.tv_date); init(); final Calendar calendar = Calendar.getInstance(); second = calendar.get(Calendar.SECOND); handler.sendMessageDelayed(handler.obtainMessage(), ( 60 - second) * 1000 ); } void init() { java.text.DateFormat df = new java.text.SimpleDateFormat( " HH:mm " ); tv_date_time.setText(df.format( new Date())); tv_week.setText(strHelpeUtil.getWeekOfDate( new Date())); strHelpeUtil str = new strHelpeUtil(getContext()); tv_date.setText(str.toString()); }}
上篇运行效果如下:
由于没有为其提供背景颜色,使用的同学可以自己为它们加上一个好看的背景颜色,效果会更加。
上面的天气组件,其实可以使用AsyncTask也是起到同样的效果,AsyncTask使用起来会觉得优雅一点,这里也顺便把一些AsyncTask在使用上一些注意事项跟大家谈一谈:
在doInBackground 里面不要直接操作UI,比如设置UI的可见性操作。在doInBackground 所在的操作只负责帮你得到数据,然后把UI处理都放在onPostExecute 里面。同时启动几个AsyncTask 注意线程加锁,使用synchronized必须每次都创建一个新的AsyncTask 对象,否则会提示“a task can be executed only once” 的错误信息。本篇的所有源码下载地址:组件
转载于:https://www.cnblogs.com/TerryBlog/archive/2011/01/26/1945747.html
