android上的一个网络接口和图片缓存框架enif简析
程序员文章站
2023-12-14 18:55:10
1.底层网络接口采用apache的httpclient连接池框架; 2.图片缓存采用基于lru的算法; 3.网络接口采用监听者模式; 4.包含图片的oom处理(及时回收处理...
1.底层网络接口采用apache的httpclient连接池框架;
2.图片缓存采用基于lru的算法;
3.网络接口采用监听者模式;
4.包含图片的oom处理(及时回收处理技术的应用);
图片核心处理类:cacheview.java
package xiaogang.enif.image;
import java.io.filterinputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.lang.ref.softreference;
import java.util.hashmap;
import java.util.concurrent.rejectedexecutionexception;
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.methods.httpget;
import xiaogang.enif.utils.httpmanager;
import xiaogang.enif.utils.ioutils;
import xiaogang.enif.utils.logutils;
import xiaogang.enif.utils.lrucache;
import android.app.activitymanager;
import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.bitmapfactory.options;
import android.graphics.canvas;
import android.graphics.drawable.bitmapdrawable;
import android.os.asynctask;
import android.text.textutils;
import android.util.attributeset;
import android.widget.imageview;
public class cacheview extends imageview {
private static final int default_res_id = 0;
private int mdefaultimage = default_res_id;
private static lrucache<string, bitmap> mlrucache;
private static hashmap<integer, softreference<bitmap>> mresimage;
private context mcontext;
private logutils mlog = logutils.getlog(cacheview.class);
public cacheview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
init(context);
}
public cacheview(context context, attributeset attrs) {
super(context, attrs);
init(context);
}
public cacheview(context context) {
super(context);
init(context);
}
private void init(context context) {
mcontext = context;
if (mlrucache == null) {
final int cachesize = getcachesize(context);
mlrucache = new lrucache<string, bitmap>(cachesize) {
@override
protected int sizeof(string key, bitmap bitmap) {
// the cache size will be measured in bytes rather than
// number of items.
return bitmap.getrowbytes() * bitmap.getheight();
}
@override
protected void entryremoved(boolean evicted, string key, bitmap oldvalue,
bitmap newvalue) {
if (evicted && oldvalue != null && !oldvalue.isrecycled()) {
oldvalue.recycle();
oldvalue = null;
}
}
};
}
if (mresimage == null) {
mresimage = new hashmap<integer, softreference<bitmap>>();
}
}
@override
protected void ondraw(canvas canvas) {
bitmapdrawable drawable = (bitmapdrawable)getdrawable();
if (drawable == null) {
setdefaultimage();
} else {
if (drawable.getbitmap() == null || drawable.getbitmap().isrecycled()) {
setdefaultimage();
}
}
try {
super.ondraw(canvas);
} catch(runtimeexception ex) {
}
}
public void setimageurl(string url, int resid) {
settag(url);
bitmap bitmap = getbitmapfromcache(url);
if (bitmap == null || bitmap.isrecycled()) {
mdefaultimage = resid;
setdefaultimage();
try {
new downloadtask().execute(url);
} catch (rejectedexecutionexception e) {
// do nothing, just keep not crash
}
} else {
setimagebitmap(bitmap);
}
}
private void setdefaultimage() {
if (mdefaultimage != default_res_id) {
setimagebitmap(getdefaultbitmap(mcontext));
}
}
private bitmap getdefaultbitmap(context context) {
softreference<bitmap> loading = mresimage.get(mdefaultimage);
if (loading == null || loading.get() == null || loading.get().isrecycled()) {
loading = new softreference<bitmap>(bitmapfactory.decoderesource(
context.getresources(), mdefaultimage));
mresimage.put(mdefaultimage, loading);
}
return loading.get();
}
private class downloadtask extends asynctask<string, void, bitmap> {
private string mparams;
@override
public bitmap doinbackground(string... params) {
mparams = params[0];
final bitmap bm = download(mparams);
addbitmaptocache(mparams, bm);
return bm;
}
@override
public void onpostexecute(bitmap bitmap) {
string tag = (string)gettag();
if (!textutils.isempty(tag) && tag.equals(mparams)) {
if (bitmap != null) {
setimagebitmap(bitmap);
}
}
}
};
/*
* an inputstream that skips the exact number of bytes provided, unless it
* reaches eof.
*/
static class flushedinputstream extends filterinputstream {
public flushedinputstream(inputstream inputstream) {
super(inputstream);
}
@override
public long skip(long n) throws ioexception {
long totalbytesskipped = 0l;
while (totalbytesskipped < n) {
long bytesskipped = in.skip(n - totalbytesskipped);
if (bytesskipped == 0l) {
int b = read();
if (b < 0) {
break; // we reached eof
} else {
bytesskipped = 1; // we read one byte
}
}
totalbytesskipped += bytesskipped;
}
return totalbytesskipped;
}
}
private bitmap download(string url) {
inputstream in = null;
httpentity entity = null;
bitmap bmp = null;
try {
final httpget get = new httpget(url);
final httpresponse response = httpmanager.execute(mcontext, get);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
entity = response.getentity();
in = entity.getcontent();
try {
bmp = getdecodebitmap(in, url);
} catch (outofmemoryerror err) {
runtime.getruntime().gc();
bmp = getdecodebitmap(in, url);
}
} else {
get.abort();
return bmp;
}
addbitmaptocache(url, bmp);
} catch (ioexception e) {
return bmp;
} finally {
ioutils.closestream(in);
}
return bmp;
}
private final bitmap getdecodebitmap(inputstream in, string url) {
options options = new options();
options.inpurgeable = true;
options.ininputshareable = true;
return bitmapfactory.decodestream(new flushedinputstream(in), null, options);
}
private final void addbitmaptocache(string url, bitmap bitmap) {
if (bitmap != null) {
mlrucache.put(url, bitmap);
runtime.getruntime().gc();
}
}
private final bitmap getbitmapfromcache(string url) {
return mlrucache.get(url);
}
private int getcachesize(context context) {
// according to the phone memory, set a proper cache size for lru cache
// dynamically.
final activitymanager am = (activitymanager)context
.getsystemservice(context.activity_service);
final int memclass = am.getmemoryclass();
int cachesize;
if (memclass <= 24) {
cachesize = (memclass << 20) / 24;
} else if (memclass <= 36) {
cachesize = (memclass << 20) / 18;
} else if (memclass <= 48) {
cachesize = (memclass << 20) / 12;
} else {
cachesize = (memclass << 20) >> 3;
}
mlog.debug("cachesize == "+cachesize);
system.out.println("cachesize == "+cachesize);
return cachesize;
}
public static void recycle() {
if (mlrucache != null && !mlrucache.isempty()) {
mlrucache.evictall();
mlrucache = null;
}
if (mresimage != null) {
for (softreference<bitmap> reference : mresimage.values()) {
bitmap bitmap = reference.get();
if (bitmap != null && !bitmap.isrecycled()) {
bitmap.recycle();
bitmap = null;
}
}
mresimage = null;
}
}
}
说明:
1)entryremoved在做bitmap recycle的时候的3个条件缺一不可;
2)ondraw里面判断图片是否被回收,如果回收,需要设置默认图片;
3)add bitmap到cache的时候runtime.getruntime().gc();的调用;
4)getcachesize可以根据手机具体的内存来动态设置我们实际需要的缓存大小;
5)退出时,记得调用recycle()方法;
网络接口核心类:wsapi.java, wscfg.java, wstask.java
<strong>package xiaogang.enif.net;
import java.util.arraylist;
import org.apache.http.message.basicnamevaluepair;
/**
* web service configuration file
* */
public class wscfg {
public static final int user_login = 0;//action
public static final int user_logout = 1;//action
public static arraylist<basicnamevaluepair> svaluepairs;//common vps
static {
svaluepairs = new arraylist<basicnamevaluepair>();
svaluepairs.add(new basicnamevaluepair("v", "1.0"));
svaluepairs.add(new basicnamevaluepair("format", "json"));
}
}</strong>
<strong>package xiaogang.enif.net;
import java.util.arraylist;
import java.util.concurrent.rejectedexecutionexception;
import org.apache.http.message.basicnamevaluepair;
import xiaogang.enif.net.wstask.tasklistener;
import android.content.context;
public class wsapi {
private wsapi() {
}
public static void execute(context context, tasklistener listener, int action,
arraylist<basicnamevaluepair> vp) {
try {
new wstask(context, listener, action, vp).execute();
} catch (rejectedexecutionexception e) {
// do nothing, just keep not crashing.
}
}
}
</strong>
<strong>package xiaogang.enif.net;
import java.io.ioexception;
import java.io.inputstream;
import java.util.arraylist;
import java.util.hashmap;
import java.util.iterator;
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.entity.urlencodedformentity;
import org.apache.http.client.methods.httppost;
import org.apache.http.message.basicnamevaluepair;
import org.json.jsonarray;
import org.json.jsonexception;
import org.json.jsonobject;
import xiaogang.enif.utils.httpmanager;
import xiaogang.enif.utils.ioutils;
import xiaogang.enif.utils.logutils;
import android.app.activity;
import android.content.context;
import android.os.asynctask;
import android.text.textutils;
public class wstask extends asynctask<void, void, object> {
private int maction;
private string merrorcode;
private object mparameter;
private context mcontext;
private tasklistener mtasklistener;
private exception mreason;
private final logutils mlog = logutils.getlog(wstask.class);
public wstask(context context, tasklistener listener, int action, object paramobject) {
mcontext = context;
mtasklistener = listener;
mparameter = paramobject;
maction = action;
}
@override
public object doinbackground(void... arg0) {
object result = null;
try {
@suppresswarnings("unchecked")
arraylist<basicnamevaluepair> vps = (arraylist<basicnamevaluepair>)mparameter;
final string jsonstring = request(mcontext, "your url", vps);
mlog.debug(jsonstring);
result = parsejson(jsonstring);
if (result != null && result instanceof string
&& textutils.isdigitsonly((string)result)) {
merrorcode = (string)result;
return null;
}
} catch (exception e) {
mreason = e;
mlog.error(e.getmessage());
return null;
}
return result;
}
@override
public void onpostexecute(object result) {
if (mcontext== null) {
cleartask();
return;
}
if (mcontext instanceof activity && ((activity) mcontext).isfinishing()) {
cleartask();
return;
}
if (result == null || mreason != null) {
mtasklistener.onfailed(maction, merrorcode, mreason);
} else {
mtasklistener.onsuccess(maction, result);
}
cleartask();
}
private string request(context context, string url, arraylist<basicnamevaluepair> vp)
throws ioexception {
final httppost post = new httppost(url);
post.setentity(new urlencodedformentity(vp, "utf_8"));
inputstream in = null;
httpentity entity = null;
try {
final httpresponse response = httpmanager.execute(context, post);
final int statuscode = response.getstatusline().getstatuscode();
if (statuscode == httpstatus.sc_ok) {
entity = response.getentity();
if (entity != null) {
in = entity.getcontent();
return ioutils.stream2string(in);
}
} else {
post.abort();
mlog.error("http code: " + response.getstatusline().getstatuscode());
}
return null;
} catch (ioexception ex) {
post.abort();
throw ex;
} catch (runtimeexception ex) {
post.abort();
throw ex;
} finally {
if(entity!=null) {
entity.consumecontent();
}
ioutils.closestream(in);
}
}
private object parsejson(string jsonstring) throws ioexception {
try {
jsonobject jobj = new jsonobject(jsonstring);
if (jobj.has("errorcode")) {
return jobj.optstring("errorcode");
}
if (jobj.has("resultlist")) {
arraylist<hashmap<string, string>> arrlist;
arrlist = new arraylist<hashmap<string, string>>();
jsonarray jsonarray = jobj.optjsonarray("resultlist");
final int len = jsonarray.length();
for (int i = 0; i < len; i++) {
final jsonobject obj = (jsonobject)jsonarray.opt(i);
arrlist.add(parse2map(obj));
}
return arrlist;
} else {
return parse2map(jobj);
}
} catch (jsonexception e) {
ioexception ioe = new ioexception("invalid json string...");
ioe.initcause(e);
throw ioe;
}
}
private hashmap<string, string> parse2map(jsonobject jsonobj) throws ioexception {
final hashmap<string, string> hashmap = new hashmap<string, string>();
@suppresswarnings("unchecked")
final iterator<string> keyiter = jsonobj.keys();
string key, value;
while (keyiter != null && keyiter.hasnext()) {
key = keyiter.next();
value = jsonobj.optstring(key);
hashmap.put(key, value);
}
return hashmap;
}
private void cleartask() {
mtasklistener = null;
mparameter = null;
mcontext = null;
}
public interface tasklistener {
public void onsuccess(int action, object result);
public void onfailed(int action, string errcode, exception ex);
}
}
</strong>
说明:
1)根据你的服务器接口实际情况,去修改parsejson方法;
2)wscfg里面可以定义接口的action;
sample:
package xiaogang.enif.ui;
import java.util.arraylist;
import org.apache.http.message.basicnamevaluepair;
import xiaogang.enif.r;
import xiaogang.enif.image.cacheview;
import xiaogang.enif.net.wsapi;
import xiaogang.enif.net.wscfg;
import xiaogang.enif.net.wstask.tasklistener;
import xiaogang.enif.widget.listsapdater;
import android.app.activity;
import android.os.bundle;
import android.widget.listview;
public class mainactivity extends activity implements tasklistener {
listview mlist;
listsapdater madapter;
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
setupviews();
}
private void setupviews() {
mlist = (listview)findviewbyid(r.id.list);
madapter = new listsapdater(this, murls);
mlist.setadapter(madapter);
final arraylist<basicnamevaluepair> vp = new arraylist<basicnamevaluepair>();
vp.addall(wscfg.svaluepairs);
vp.add(new basicnamevaluepair("imei", "123"));
vp.add(new basicnamevaluepair("imsi", "123"));
wsapi.execute(this, this, wscfg.user_login, vp);
}
@override
protected void ondestroy() {
super.ondestroy();
madapter.recycle();
cacheview.recycle();
}
private string[] murls = {
"http://a3.twimg.com/profile_images/670625317/aam-logo-v3-twitter.png",
"http://a3.twimg.com/profile_images/740897825/androidcast-350_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo_normal.png",
"http://a3.twimg.com/profile_images/681537837/smallavatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/ot_icon_090918_android_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo.png",
"http://a3.twimg.com/profile_images/681537837/smallavatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73_normal.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/ot_icon_090918_android.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png"
};
@override
public void onsuccess(int action, object result) {
switch (action) {
case wscfg.user_login:
break;
case wscfg.user_logout:
break;
}
}
@override
public void onfailed(int action, string errcode, exception ex) {
switch (action) {
case wscfg.user_login:
break;
case wscfg.user_logout:
break;
}
}
}
package xiaogang.enif.widget;
import xiaogang.enif.r;
import xiaogang.enif.image.cacheview;
import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.textview;
public class listsapdater extends baseadapter {
private string[] murls;
private layoutinflater minflater;
public listsapdater(context context, string[] urls) {
murls = urls;
minflater = (layoutinflater)context.getsystemservice(context.layout_inflater_service);
}
@override
public int getcount() {
return murls.length;
}
@override
public object getitem(int position) {
return position;
}
@override
public long getitemid(int position) {
return position;
}
@override
public view getview(int position, view convertview, viewgroup parent) {
viewholder holder = null;
if (null == convertview) {
holder = new viewholder();
convertview = minflater.inflate(r.layout.item, null);
holder.view = (cacheview)convertview.findviewbyid(r.id.image);
holder.text = (textview)convertview.findviewbyid(r.id.text);
convertview.settag(holder);
} else {
holder = (viewholder)convertview.gettag();
}
holder.text.settext("item "+position);
holder.view.setimageurl(murls[position], r.drawable.stub);
return convertview;
}
public void recycle() {
murls = null;
minflater = null;
}
private class viewholder {
cacheview view;
textview text;
}
}
main.xml和item.xml
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<listview
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</linearlayout>
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<xiaogang.enif.image.cacheview
android:id="@+id/image"
android:layout_width="50dip"
android:layout_height="50dip"
android:scaletype="centercrop"
android:src="@drawable/stub" />
<textview
android:id="@+id/text"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:layout_marginleft="10dip"
android:layout_weight="1"
android:textsize="20dip" />
</linearlayout>
例子的效果图如下:
2.图片缓存采用基于lru的算法;
3.网络接口采用监听者模式;
4.包含图片的oom处理(及时回收处理技术的应用);
图片核心处理类:cacheview.java
复制代码 代码如下:
package xiaogang.enif.image;
import java.io.filterinputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.lang.ref.softreference;
import java.util.hashmap;
import java.util.concurrent.rejectedexecutionexception;
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.methods.httpget;
import xiaogang.enif.utils.httpmanager;
import xiaogang.enif.utils.ioutils;
import xiaogang.enif.utils.logutils;
import xiaogang.enif.utils.lrucache;
import android.app.activitymanager;
import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.bitmapfactory.options;
import android.graphics.canvas;
import android.graphics.drawable.bitmapdrawable;
import android.os.asynctask;
import android.text.textutils;
import android.util.attributeset;
import android.widget.imageview;
public class cacheview extends imageview {
private static final int default_res_id = 0;
private int mdefaultimage = default_res_id;
private static lrucache<string, bitmap> mlrucache;
private static hashmap<integer, softreference<bitmap>> mresimage;
private context mcontext;
private logutils mlog = logutils.getlog(cacheview.class);
public cacheview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
init(context);
}
public cacheview(context context, attributeset attrs) {
super(context, attrs);
init(context);
}
public cacheview(context context) {
super(context);
init(context);
}
private void init(context context) {
mcontext = context;
if (mlrucache == null) {
final int cachesize = getcachesize(context);
mlrucache = new lrucache<string, bitmap>(cachesize) {
@override
protected int sizeof(string key, bitmap bitmap) {
// the cache size will be measured in bytes rather than
// number of items.
return bitmap.getrowbytes() * bitmap.getheight();
}
@override
protected void entryremoved(boolean evicted, string key, bitmap oldvalue,
bitmap newvalue) {
if (evicted && oldvalue != null && !oldvalue.isrecycled()) {
oldvalue.recycle();
oldvalue = null;
}
}
};
}
if (mresimage == null) {
mresimage = new hashmap<integer, softreference<bitmap>>();
}
}
@override
protected void ondraw(canvas canvas) {
bitmapdrawable drawable = (bitmapdrawable)getdrawable();
if (drawable == null) {
setdefaultimage();
} else {
if (drawable.getbitmap() == null || drawable.getbitmap().isrecycled()) {
setdefaultimage();
}
}
try {
super.ondraw(canvas);
} catch(runtimeexception ex) {
}
}
public void setimageurl(string url, int resid) {
settag(url);
bitmap bitmap = getbitmapfromcache(url);
if (bitmap == null || bitmap.isrecycled()) {
mdefaultimage = resid;
setdefaultimage();
try {
new downloadtask().execute(url);
} catch (rejectedexecutionexception e) {
// do nothing, just keep not crash
}
} else {
setimagebitmap(bitmap);
}
}
private void setdefaultimage() {
if (mdefaultimage != default_res_id) {
setimagebitmap(getdefaultbitmap(mcontext));
}
}
private bitmap getdefaultbitmap(context context) {
softreference<bitmap> loading = mresimage.get(mdefaultimage);
if (loading == null || loading.get() == null || loading.get().isrecycled()) {
loading = new softreference<bitmap>(bitmapfactory.decoderesource(
context.getresources(), mdefaultimage));
mresimage.put(mdefaultimage, loading);
}
return loading.get();
}
private class downloadtask extends asynctask<string, void, bitmap> {
private string mparams;
@override
public bitmap doinbackground(string... params) {
mparams = params[0];
final bitmap bm = download(mparams);
addbitmaptocache(mparams, bm);
return bm;
}
@override
public void onpostexecute(bitmap bitmap) {
string tag = (string)gettag();
if (!textutils.isempty(tag) && tag.equals(mparams)) {
if (bitmap != null) {
setimagebitmap(bitmap);
}
}
}
};
/*
* an inputstream that skips the exact number of bytes provided, unless it
* reaches eof.
*/
static class flushedinputstream extends filterinputstream {
public flushedinputstream(inputstream inputstream) {
super(inputstream);
}
@override
public long skip(long n) throws ioexception {
long totalbytesskipped = 0l;
while (totalbytesskipped < n) {
long bytesskipped = in.skip(n - totalbytesskipped);
if (bytesskipped == 0l) {
int b = read();
if (b < 0) {
break; // we reached eof
} else {
bytesskipped = 1; // we read one byte
}
}
totalbytesskipped += bytesskipped;
}
return totalbytesskipped;
}
}
private bitmap download(string url) {
inputstream in = null;
httpentity entity = null;
bitmap bmp = null;
try {
final httpget get = new httpget(url);
final httpresponse response = httpmanager.execute(mcontext, get);
if (response.getstatusline().getstatuscode() == httpstatus.sc_ok) {
entity = response.getentity();
in = entity.getcontent();
try {
bmp = getdecodebitmap(in, url);
} catch (outofmemoryerror err) {
runtime.getruntime().gc();
bmp = getdecodebitmap(in, url);
}
} else {
get.abort();
return bmp;
}
addbitmaptocache(url, bmp);
} catch (ioexception e) {
return bmp;
} finally {
ioutils.closestream(in);
}
return bmp;
}
private final bitmap getdecodebitmap(inputstream in, string url) {
options options = new options();
options.inpurgeable = true;
options.ininputshareable = true;
return bitmapfactory.decodestream(new flushedinputstream(in), null, options);
}
private final void addbitmaptocache(string url, bitmap bitmap) {
if (bitmap != null) {
mlrucache.put(url, bitmap);
runtime.getruntime().gc();
}
}
private final bitmap getbitmapfromcache(string url) {
return mlrucache.get(url);
}
private int getcachesize(context context) {
// according to the phone memory, set a proper cache size for lru cache
// dynamically.
final activitymanager am = (activitymanager)context
.getsystemservice(context.activity_service);
final int memclass = am.getmemoryclass();
int cachesize;
if (memclass <= 24) {
cachesize = (memclass << 20) / 24;
} else if (memclass <= 36) {
cachesize = (memclass << 20) / 18;
} else if (memclass <= 48) {
cachesize = (memclass << 20) / 12;
} else {
cachesize = (memclass << 20) >> 3;
}
mlog.debug("cachesize == "+cachesize);
system.out.println("cachesize == "+cachesize);
return cachesize;
}
public static void recycle() {
if (mlrucache != null && !mlrucache.isempty()) {
mlrucache.evictall();
mlrucache = null;
}
if (mresimage != null) {
for (softreference<bitmap> reference : mresimage.values()) {
bitmap bitmap = reference.get();
if (bitmap != null && !bitmap.isrecycled()) {
bitmap.recycle();
bitmap = null;
}
}
mresimage = null;
}
}
}
说明:
1)entryremoved在做bitmap recycle的时候的3个条件缺一不可;
2)ondraw里面判断图片是否被回收,如果回收,需要设置默认图片;
3)add bitmap到cache的时候runtime.getruntime().gc();的调用;
4)getcachesize可以根据手机具体的内存来动态设置我们实际需要的缓存大小;
5)退出时,记得调用recycle()方法;
网络接口核心类:wsapi.java, wscfg.java, wstask.java
复制代码 代码如下:
<strong>package xiaogang.enif.net;
import java.util.arraylist;
import org.apache.http.message.basicnamevaluepair;
/**
* web service configuration file
* */
public class wscfg {
public static final int user_login = 0;//action
public static final int user_logout = 1;//action
public static arraylist<basicnamevaluepair> svaluepairs;//common vps
static {
svaluepairs = new arraylist<basicnamevaluepair>();
svaluepairs.add(new basicnamevaluepair("v", "1.0"));
svaluepairs.add(new basicnamevaluepair("format", "json"));
}
}</strong>
复制代码 代码如下:
<strong>package xiaogang.enif.net;
import java.util.arraylist;
import java.util.concurrent.rejectedexecutionexception;
import org.apache.http.message.basicnamevaluepair;
import xiaogang.enif.net.wstask.tasklistener;
import android.content.context;
public class wsapi {
private wsapi() {
}
public static void execute(context context, tasklistener listener, int action,
arraylist<basicnamevaluepair> vp) {
try {
new wstask(context, listener, action, vp).execute();
} catch (rejectedexecutionexception e) {
// do nothing, just keep not crashing.
}
}
}
</strong>
复制代码 代码如下:
<strong>package xiaogang.enif.net;
import java.io.ioexception;
import java.io.inputstream;
import java.util.arraylist;
import java.util.hashmap;
import java.util.iterator;
import org.apache.http.httpentity;
import org.apache.http.httpresponse;
import org.apache.http.httpstatus;
import org.apache.http.client.entity.urlencodedformentity;
import org.apache.http.client.methods.httppost;
import org.apache.http.message.basicnamevaluepair;
import org.json.jsonarray;
import org.json.jsonexception;
import org.json.jsonobject;
import xiaogang.enif.utils.httpmanager;
import xiaogang.enif.utils.ioutils;
import xiaogang.enif.utils.logutils;
import android.app.activity;
import android.content.context;
import android.os.asynctask;
import android.text.textutils;
public class wstask extends asynctask<void, void, object> {
private int maction;
private string merrorcode;
private object mparameter;
private context mcontext;
private tasklistener mtasklistener;
private exception mreason;
private final logutils mlog = logutils.getlog(wstask.class);
public wstask(context context, tasklistener listener, int action, object paramobject) {
mcontext = context;
mtasklistener = listener;
mparameter = paramobject;
maction = action;
}
@override
public object doinbackground(void... arg0) {
object result = null;
try {
@suppresswarnings("unchecked")
arraylist<basicnamevaluepair> vps = (arraylist<basicnamevaluepair>)mparameter;
final string jsonstring = request(mcontext, "your url", vps);
mlog.debug(jsonstring);
result = parsejson(jsonstring);
if (result != null && result instanceof string
&& textutils.isdigitsonly((string)result)) {
merrorcode = (string)result;
return null;
}
} catch (exception e) {
mreason = e;
mlog.error(e.getmessage());
return null;
}
return result;
}
@override
public void onpostexecute(object result) {
if (mcontext== null) {
cleartask();
return;
}
if (mcontext instanceof activity && ((activity) mcontext).isfinishing()) {
cleartask();
return;
}
if (result == null || mreason != null) {
mtasklistener.onfailed(maction, merrorcode, mreason);
} else {
mtasklistener.onsuccess(maction, result);
}
cleartask();
}
private string request(context context, string url, arraylist<basicnamevaluepair> vp)
throws ioexception {
final httppost post = new httppost(url);
post.setentity(new urlencodedformentity(vp, "utf_8"));
inputstream in = null;
httpentity entity = null;
try {
final httpresponse response = httpmanager.execute(context, post);
final int statuscode = response.getstatusline().getstatuscode();
if (statuscode == httpstatus.sc_ok) {
entity = response.getentity();
if (entity != null) {
in = entity.getcontent();
return ioutils.stream2string(in);
}
} else {
post.abort();
mlog.error("http code: " + response.getstatusline().getstatuscode());
}
return null;
} catch (ioexception ex) {
post.abort();
throw ex;
} catch (runtimeexception ex) {
post.abort();
throw ex;
} finally {
if(entity!=null) {
entity.consumecontent();
}
ioutils.closestream(in);
}
}
private object parsejson(string jsonstring) throws ioexception {
try {
jsonobject jobj = new jsonobject(jsonstring);
if (jobj.has("errorcode")) {
return jobj.optstring("errorcode");
}
if (jobj.has("resultlist")) {
arraylist<hashmap<string, string>> arrlist;
arrlist = new arraylist<hashmap<string, string>>();
jsonarray jsonarray = jobj.optjsonarray("resultlist");
final int len = jsonarray.length();
for (int i = 0; i < len; i++) {
final jsonobject obj = (jsonobject)jsonarray.opt(i);
arrlist.add(parse2map(obj));
}
return arrlist;
} else {
return parse2map(jobj);
}
} catch (jsonexception e) {
ioexception ioe = new ioexception("invalid json string...");
ioe.initcause(e);
throw ioe;
}
}
private hashmap<string, string> parse2map(jsonobject jsonobj) throws ioexception {
final hashmap<string, string> hashmap = new hashmap<string, string>();
@suppresswarnings("unchecked")
final iterator<string> keyiter = jsonobj.keys();
string key, value;
while (keyiter != null && keyiter.hasnext()) {
key = keyiter.next();
value = jsonobj.optstring(key);
hashmap.put(key, value);
}
return hashmap;
}
private void cleartask() {
mtasklistener = null;
mparameter = null;
mcontext = null;
}
public interface tasklistener {
public void onsuccess(int action, object result);
public void onfailed(int action, string errcode, exception ex);
}
}
</strong>
说明:
1)根据你的服务器接口实际情况,去修改parsejson方法;
2)wscfg里面可以定义接口的action;
sample:
复制代码 代码如下:
package xiaogang.enif.ui;
import java.util.arraylist;
import org.apache.http.message.basicnamevaluepair;
import xiaogang.enif.r;
import xiaogang.enif.image.cacheview;
import xiaogang.enif.net.wsapi;
import xiaogang.enif.net.wscfg;
import xiaogang.enif.net.wstask.tasklistener;
import xiaogang.enif.widget.listsapdater;
import android.app.activity;
import android.os.bundle;
import android.widget.listview;
public class mainactivity extends activity implements tasklistener {
listview mlist;
listsapdater madapter;
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
setupviews();
}
private void setupviews() {
mlist = (listview)findviewbyid(r.id.list);
madapter = new listsapdater(this, murls);
mlist.setadapter(madapter);
final arraylist<basicnamevaluepair> vp = new arraylist<basicnamevaluepair>();
vp.addall(wscfg.svaluepairs);
vp.add(new basicnamevaluepair("imei", "123"));
vp.add(new basicnamevaluepair("imsi", "123"));
wsapi.execute(this, this, wscfg.user_login, vp);
}
@override
protected void ondestroy() {
super.ondestroy();
madapter.recycle();
cacheview.recycle();
}
private string[] murls = {
"http://a3.twimg.com/profile_images/670625317/aam-logo-v3-twitter.png",
"http://a3.twimg.com/profile_images/740897825/androidcast-350_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo_normal.png",
"http://a3.twimg.com/profile_images/681537837/smallavatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/ot_icon_090918_android_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf_normal.jpg",
"http://a1.twimg.com/profile_images/97470808/icon.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/768060227/ap4u_normal.jpg",
"http://a1.twimg.com/profile_images/74724754/android_logo.png",
"http://a3.twimg.com/profile_images/681537837/smallavatarx150_normal.png",
"http://a1.twimg.com/profile_images/63737974/2008-11-06_1637_normal.png",
"http://a3.twimg.com/profile_images/548410609/icon_8_73_normal.png",
"http://a1.twimg.com/profile_images/612232882/nexusoneavatar_normal.jpg",
"http://a1.twimg.com/profile_images/213722080/bugdroid-phone_normal.png",
"http://a1.twimg.com/profile_images/645523828/ot_icon_090918_android.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/349012784/android_logo_small_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a1.twimg.com/profile_images/850960042/elandroidelibre-logo_300x300_normal.jpg",
"http://a1.twimg.com/profile_images/655119538/andbook_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a3.twimg.com/profile_images/956404323/androinica-avatar_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/121630227/droid_normal.jpg",
"http://a1.twimg.com/profile_images/957149154/twitterhalf.jpg",
"http://a1.twimg.com/profile_images/97470808/icon_normal.png",
"http://a3.twimg.com/profile_images/511790713/ag_normal.png",
"http://a1.twimg.com/profile_images/909231146/android_biz_man_normal.png",
"http://a3.twimg.com/profile_images/72774055/androidhomme-logo_normal.jpg",
"http://a1.twimg.com/profile_images/841338368/ea-twitter-icon_normal.png",
"http://a3.twimg.com/profile_images/64827025/android-wallpaper6_2560x160_normal.png",
"http://a3.twimg.com/profile_images/77641093/androidplanet_normal.png"
};
@override
public void onsuccess(int action, object result) {
switch (action) {
case wscfg.user_login:
break;
case wscfg.user_logout:
break;
}
}
@override
public void onfailed(int action, string errcode, exception ex) {
switch (action) {
case wscfg.user_login:
break;
case wscfg.user_logout:
break;
}
}
}
复制代码 代码如下:
package xiaogang.enif.widget;
import xiaogang.enif.r;
import xiaogang.enif.image.cacheview;
import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.textview;
public class listsapdater extends baseadapter {
private string[] murls;
private layoutinflater minflater;
public listsapdater(context context, string[] urls) {
murls = urls;
minflater = (layoutinflater)context.getsystemservice(context.layout_inflater_service);
}
@override
public int getcount() {
return murls.length;
}
@override
public object getitem(int position) {
return position;
}
@override
public long getitemid(int position) {
return position;
}
@override
public view getview(int position, view convertview, viewgroup parent) {
viewholder holder = null;
if (null == convertview) {
holder = new viewholder();
convertview = minflater.inflate(r.layout.item, null);
holder.view = (cacheview)convertview.findviewbyid(r.id.image);
holder.text = (textview)convertview.findviewbyid(r.id.text);
convertview.settag(holder);
} else {
holder = (viewholder)convertview.gettag();
}
holder.text.settext("item "+position);
holder.view.setimageurl(murls[position], r.drawable.stub);
return convertview;
}
public void recycle() {
murls = null;
minflater = null;
}
private class viewholder {
cacheview view;
textview text;
}
}
main.xml和item.xml
复制代码 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<listview
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</linearlayout>
复制代码 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<xiaogang.enif.image.cacheview
android:id="@+id/image"
android:layout_width="50dip"
android:layout_height="50dip"
android:scaletype="centercrop"
android:src="@drawable/stub" />
<textview
android:id="@+id/text"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:layout_marginleft="10dip"
android:layout_weight="1"
android:textsize="20dip" />
</linearlayout>
例子的效果图如下: