return icon;
} } }
} catch (IllegalArgumentException ex) { request.abort(); } catch (IOException ex) { request.abort(); } finally { client.close(); }
return null;
} @Override
protected void onCancelled() { if (mCursor != null) { mCursor.close();
}
} @Override
public void onPostExecute(Bitmap icon) { if (mActivity != null) {
mActivity.mTouchIconLoader = null;
} if (icon == null || mCursor == null || isCancelled()) { return;
} 最终图标要保存到浏览器的内部数据库中,系统程序均保存为SQLite格式,Browser也不例外,因为图片是二进制的所以使用字节数组存储数据库的BLOB类型 final ByteArrayOutputStream os = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.PNG, 100, os); //将Bitmap压缩成PNG编码,质量为100%存储
ContentValues values = new ContentValues(); //构造SQLite的Content对象,这里也可以使用raw sql代替
values.put(Browser.BookmarkColumns.TOUCH_ICON,os.toByteArray()); //写入数据库的Browser.BookmarkColumns.TOUCH_ICON字段 if (mCursor.moveToFirst()) {
do {
mContentResolver.update(ContentUris.withAppendedId(Browser.BOOKMARKS_URI, mCursor.getInt(0)),values, null, null); } while (mCursor.moveToNext()); }
mCursor.close(); }
} 本次Android开发网通过两个AsyncTask类演示了多种类型的任务构造,这里大家注意返回类型,本节演示了Android平台上Content Provider、AsyncTask、Bitmap、HTTP以及Stream
的相关操作,大家如何想很快提高开发水平其实只要理解Google如何去实现Android系统常规构架就可以轻松入门谷歌移动平台。
50. Android自定义View实例AnalogClock源码
针对Android底层View的直接构造很多网友没有实战经验,本次Android开发网结合目前平台开源代码一起通过AnalogClock类来理解View的直接继承。AnalogClock就是Home Screen上的那个带有两根指针的表盘类。它的实现我们直接从开源代码可以了解到: public class
AnalogClock extends View {
private Time mCalendar; private Drawable mHourHand; //时针 private Drawable mMinuteHand; //分针
private Drawable mDial; //表盘背景 private int mDialWidth; //表盘宽度
private int mDialHeight; //表盘高度 private boolean mAttached; //附着状态 private final Handler mHandler = new Handler(); //定一个Handler类实现更新时间 private float mMinutes;
private float mHour;
private boolean mChanged; //时间是否改变 public AnalogClock(Context context) { this(context, null);
} public AnalogClock(Context context, AttributeSet attrs) { this(context, attrs, 0);
} public AnalogClock(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);
Resources r = mContext.getResources(); TypedArray a =
context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.AnalogClock, defStyle, 0); mDial = a.getDrawable(com.android.internal.R.styleable.AnalogClock_dial); //加载表盘资源
if (mDial == null) {
mDial = r.getDrawable(com.android.internal.R.drawable.clock_dial); } mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); //加载时针图片资源 if (mHourHand == null) {
mHourHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_hour); } mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); //加载分针图片 if (mMinuteHand == null) {
mMinuteHand = r.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } mCalendar = new Time(); //获取当前系统时间 mDialWidth = mDial.getIntrinsicWidth(); //获取表盘图片的宽度
mDialHeight = mDial.getIntrinsicHeight(); //高度,同上 } @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow(); if (!mAttached) {
mAttached = true;
IntentFilter filter = new IntentFilter(); //注册一个消息过滤器,获取时间改变、时区改变的action filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
} mCalendar = new Time(); onTimeChanged(); } @Override
protected void onDetachedFromWindow() { super.onDetachedFromWindow();
if (mAttached) {
getContext().unregisterReceiver(mIntentReceiver); //反注册消息过滤器 mAttached = false; }
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec); float hScale = 1.0f; float vScale = 1.0f; if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) {
hScale = (float) widthSize / (float) mDialWidth;
} if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) { vScale = (float )heightSize / (float) mDialHeight;
} float scale = Math.min(hScale, vScale); setMeasuredDimension(resolveSize((int) (mDialWidth * scale), widthMeasureSpec),
resolveSize((int) (mDialHeight * scale), heightMeasureSpec)); } @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh); mChanged = true;
} 主要的绘图重写View的onDraw方法,我们可以看到通过canvas实例直接屏幕 @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); boolean changed = mChanged; if (changed) {
mChanged = false;
} int availableWidth = mRight - mLeft;
int availableHeight = mBottom - mTop; int x = availableWidth / 2; int y = availableHeight / 2; final Drawable dial = mDial; int w = dial.getIntrinsicWidth();
int h = dial.getIntrinsicHeight(); boolean scaled = false; if (availableWidth < w || availableHeight < h) {
scaled = true;
float scale = Math.min((float) availableWidth / (float) w, (float) availableHeight / (float) h);
canvas.save();
canvas.scale(scale, scale, x, y); } if (changed) {
dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); }
dial.draw(canvas); canvas.save();
canvas.rotate(mHour / 12.0f * 360.0f, x, y); //计算时针旋转的角度,android123提示就是那个时针图片的旋转角度,直接反应的就是表盘上那个针的时间
final Drawable hourHand = mHourHand; if (changed) {
w = hourHand.getIntrinsicWidth(); h = hourHand.getIntrinsicHeight();
hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); }
hourHand.draw(canvas);
canvas.restore(); canvas.save();
canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); //同理,分针旋转的角度 final Drawable minuteHand = mMinuteHand; if (changed) {
w = minuteHand.getIntrinsicWidth(); h = minuteHand.getIntrinsicHeight();
minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); }
minuteHand.draw(canvas);
canvas.restore(); if (scaled) {
canvas.restore(); }
} private void onTimeChanged() { //获取时间改变,计算当前的时分秒 mCalendar.setToNow(); int hour = mCalendar.hour; int minute = mCalendar.minute;
int second = mCalendar.second; mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f;
mChanged = true;
} private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { //监听获取时间改变action
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra(\
mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); } onTimeChanged(); //获取新的时间
invalidate(); //刷新屏幕,强制类调用onDraw方法实现分针时针的走动
}
}; 看了本例根据,Android开发很简单吧,感兴趣的网友可以为本程序加入一个秒针,不过Android123提醒网友的是可能对于电池,以及系统运行效率产生一定的影响,不过作为练习大家可以试一试。
51. ArrayList LinkedList Set HashMap介绍
在Android开发中我们经常需要对数据进行分类和操作,对于轻量级的数据存储我们可能不需要动用SQLite或效率以及类库不完善的XML,由于SharedPreferences不具备数据枚举方法,如果仅仅是一个String或Int数组可以通过一个标记分割设计外,我们还是主要来看看Android或者说Java提供的基础数据类型辅助类ArrayList LinkedList Set HashMap的介绍,如果你熟悉C++的STL或Boost库可以略过本文。 在Java中提供了Collection和Map接口。其中List和Set继承了Collection接口;同时用Vector、ArrayList、LinkedList三个类实现List接口,HashSet、TreeSet实现Set接口。直接有HashTable、HashMap、TreeMap实现Map接口。 Vector基于Array的List,性能也就不可能超越Array,并且Vector是“sychronized”的,这个也是Vector和ArrayList的唯一的区别。 ArrayList:同Vector一样是一个基于Array的,但是不同的是ArrayList不是同步的。所以在性能上要比Vector优越一些。Android123提示大家适用于顺序性的查找 LinkedList:不同于前面两种List,它不是基于Array的,作为链表数据结构方式,所以不受Array性能的限制。当对LinkedList做添加,删除动作的时候只要更改nextNode的相关信息就可以实现了所以它适合于进行频繁进行插入和删除操作。这就是LinkedList的优势,当然对于元素的位置获取等方面就逊色很多。 List: 1. 所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[ tom,1,c ]; 2. 所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ]; 3. 所有的List中可以有null元素,例如[ tom,null,1 ]; 4. 基于Array的List(Vector,ArrayList)适合查询,而LinkedList(链表)适合添加,删除操作。 虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以Array为基础。但是Set则是在HashMap的基础上来实现的,这个就是Set和List的根本区别。 HashSet:HashSet的存储方式是把HashMap中的Key作为Set的对应存储项,HashMap的key是不能有重复的。HashSet能快速定位一个元素,但是放到HashSet中的对象需要实现hashCode()方法0。 TreeSet:将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的。TreeSet不同于HashSet的根本是TreeSet是有序的。它是通过SortedMap来实现的。 Set总结: 1. Set实现的基础是Map(HashMap); 2. Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象,不能包含两个元素e1、e2(e1.equals(e2))。 Map是一种把键对象和值对象进行关联的容器,Map有两种比较常用的实现: HashTable、HashMap和TreeMap。 HashMap也用到了哈希码的算法,以便快速查找一个键, TreeMap则是对键按序存放,因此它有一些扩展的方法,比如firstKey(),lastKey()等。 HashMap和Hashtable的区别。 HashMap允许空(null)键(key)或值(value),由于非线程安全,效率上可能高于Hashtable。 Hashtable不允许空(null)键(key)或值(value)。 有关更多实用的Android开发技巧我们将在后面的文章中着重介绍。 52. ConditionVariable Android线程同步
ConditionVariable类位于android.os.ConditionVariable,它可以帮助Android线程同步。在SDK上的介绍ConditionVariable不同于标准Java位于java.lang.Object wait() 和 notify() ,这个类可以等待自己,这就意味着 open(), close() 和 block() 可能会假死 ,如果使用ConditionVariable类的open()在调用 block() 之前, block() 将不会阻塞,相反将会返回立即。 该类一共有4个方法 boolean block(long timeout) 阻止当前线程知道条件是open,或直到超时,这里参数long timeout为超时设置,Android123提示大家如果你们从事过Win32开发,这个方法类似DWORD WaitForSingleObject(HANDLE