OuNuo0 2015-08-26
想实现一个类似于微信联系人列表的功能。网上查看了很多实现方案,主要有以下2种方案:
1.在服务器端将图片的文件流通过base64编码,再经过json/xml数据格式传送给Android客户端,客户端对图片流进行解码,使用ImagView的setImageBitmap()方法渲染ImageView。
2.使用异步加载方式来 加载头像,并将头像图片存入缓存文件中。
第一种方式这里就不进行说明了。下面主要对第二种实现方案来说明。考虑到用户一次加载较多数据会影响性能,这里加入了批量加载。
先上传效果图:

当ListView滚动到最后一行时会加载新的数据,并提示加载等待信息。还有一种常见的做法是在ListView底部放置【加载更多】按钮来触发继续加载操作。
具体是实现步骤如下:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal">
<ProgressBar
android:layout_width="40dp"
android:layout_height="40dp"
style="@android:style/Widget.ProgressBar.Small"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="数据加载中。。。。。。"/>
</LinearLayout> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
tools:context="com.example.xinxin.pandachild.NearFragment">
<ListView
android:id="@+id/nearListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
></ListView>
</FrameLayout><LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_margin="10dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/logoImage"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="10dp"/>
<TextView
android:id="@+id/userName"
android:textSize="28sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"/>
</LinearLayout> /**
* Created by xinxin on 2015/8/24.
*/
public class NearAdapter extends BaseAdapter {
private List<PUser> list;
private Context context;
private LayoutInflater layoutInflater;
private File catchFile;
private String catchPath;
public NearAdapter(Context context, List<PUser> list) {
this.context = context;
this.list = list;
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
initCatchFile();
}
/**
* 初始化缓存目录
*/
private void initCatchFile(){
catchPath = this.context.getCacheDir().getAbsolutePath() + "/png";
catchFile = new File(catchPath);
if (!catchFile.exists()) catchFile.mkdirs();
}
//该方法在批量加载ListView数据时使用
public void addData(List<PUser> list) {
if (this.list == null) this.list = new ArrayList<>();
this.list.addAll(list);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
/**
* 重写父类方法
* 使用缓存将ListView条目布局进行缓存
*/
public View getView(int position, View convertView, ViewGroup parent) {
ImageView logImage = null;
TextView userName = null;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.near_list_item, null);
logImage = (ImageView) convertView.findViewById(R.id.logoImage);
userName = (TextView) convertView.findViewById(R.id.userName);
ViewCatch viewCatch = new ViewCatch();
viewCatch.logoImage = logImage;
viewCatch.userNaem = userName;
convertView.setTag(viewCatch);
} else {
ViewCatch viewCatch = (ViewCatch) convertView.getTag();
logImage = viewCatch.logoImage;
userName = viewCatch.userNaem;
}
PUser pUser = list.get(position);
userName.setText(pUser.getUserName());
//异步加载图片
asyncImageLoad(logImage, pUser.getLogoUrl());
return convertView;
}
/**
* 缓存ListView条目类
*/
class ViewCatch {
public ImageView logoImage;
public TextView userNaem;
}
/**
* 异步加载图片
* @param imageView
* @param url
*/
public void asyncImageLoad(final ImageView imageView, final String url) {
AsyncTask<Integer, Integer, Uri> asyncTask = new AsyncTask<Integer, Integer, Uri>() {
@Override
protected Uri doInBackground(Integer... params) {//执行加载图片URI操作,并返回图片URI
return getImageUri(url);
}
@Override
protected void onPostExecute(Uri uri) {//跟新主线程UI
if (uri != null)
if (imageView != null)
imageView.setImageURI(uri);
}
};
asyncTask.execute();
}
/**
* 获得图片URI
* 加载过的文件会存放在缓存目录
* @param url
* @return
*/
private Uri getImageUri(String url) {
Uri imageUri = null;
//将图片地址进行MD5,
String catchFileName = catchPath + "/" + MD5.GetMD5Code(url) + ".png";
if (!FileUtil.exists(catchFileName)) {//判断缓存目录是否存在该图片资源
FileUtil.writeFile(catchFileName, HttpUtil.getInputStream(url, null));
}
//将文件类型转成URI格式
imageUri = Uri.fromFile(new File(catchFileName));
return imageUri;
}
} public class NearFragment extends Fragment {
private Context context;
private ListView listView;
private NearAdapter adapter;
private boolean loadFinished;
private boolean loadAll;
private LinearLayout footer;
private PUserService pUserService = new PUserService();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this.getActivity();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_near, container, false);
footer = (LinearLayout) inflater.inflate(R.layout.footer, null);
listView = (ListView) view.findViewById(R.id.nearListView);
setOnScrollListener();
new Thread(new Runnable() {//启动子线程初始化ListView,防止主线程响应超时
@Override
public void run() {
try {
List<PUser> list = pUserService.getUserList();//获得数据
handler.sendMessage(handler.obtainMessage(100, list));//发送消息
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
return view;
}
/**
* 处理ListView初始化
*/
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
List<PUser> list = (List<PUser>) msg.obj;
if (list != null) {
listView.addFooterView(footer);//在实例化前添加footer,此处必须
adapter = new NearAdapter(context, list);
listView.setAdapter(adapter);
listView.removeFooterView(footer);//去除footer
}
}
};
/**
* 处理批量加载
*/
Handler scrollHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
List<PUser> list = (List<PUser>) msg.obj;
if (list != null && list.size() > 0) {
adapter.addData(list);
adapter.notifyDataSetChanged();
loadFinished = true;
}
listView.removeFooterView(footer);
}
};
/**
* 实现LilstView批量加载数据
*/
private void setOnScrollListener() {
loadFinished = true;
loadAll = false;
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
private boolean finished;//可以控制ListView滚动是否完全停止
private int pageSize = 20;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
finished = true;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (!loadAll && totalItemCount > 0 && (firstVisibleItem + visibleItemCount) == totalItemCount && loadFinished) {//为了测试方便,没有判断finished;
listView.addFooterView(footer);//添加等待进度条
finished = false;
loadFinished = false;
final int startNo = totalItemCount;
new Thread(new Runnable() {//启动子线程,加载数据
@Override
public void run() {
//获得分页数据
List<PUser> list = pUserService.getUserList(startNo + 1, pageSize + startNo);
if (list != null && list.size() > 0) {
//不处理
} else {
loadAll = true;
}
//发送消息给主线程
scrollHandler.sendMessage(scrollHandler.obtainMessage(100, list));
}
}).start();
}
}
});
}
} 说明:这里只给出了主要的实现方式,及部分代码,PUser 及 业务处理类就不贴了,当然也不足与待优化的地方。继续学习!