欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android上的一个网络接口和图片缓存框架enif简析

程序员文章站 2023-12-04 11:28:34
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>

例子的效果图如下
android上的一个网络接口和图片缓存框架enif简析