您的位置:首页 > 其它

ListView 异步加载头像与ListView批量加载的实现

2015-08-26 23:11 267 查看

想实现一个类似于微信联系人列表的功能。网上查看了很多实现方案,主要有以下2种方案:

1.在服务器端将图片的文件流通过base64编码,再经过json/xml数据格式传送给Android客户端,客户端对图片流进行解码,使用ImagView的setImageBitmap()方法渲染ImageView。

  • 优点:图片数据可以跟随对象(javaBean)传送,解析操作比较简单。
  • 缺点:此方案只适传送较小的图片,而且传送数据时要注意base64编码产生的空格问题(可以再进行URL编码)。

2.使用异步加载方式来 加载头像,并将头像图片存入缓存文件中。

  • 优点:避免了上一种只能传输较小图片的问题。使用异步方式避免了UI无响应,带来更好的用户体验。
  • 缺点:实现方式较上一种复杂。

第一种方式这里就不进行说明了。下面主要对第二种实现方案来说明。考虑到用户一次加载较多数据会影响性能,这里加入了批量加载。

先上传效果图:



 

 当ListView滚动到最后一行时会加载新的数据,并提示加载等待信息。还有一种常见的做法是在ListView底部放置【加载更多】按钮来触发继续加载操作。

 具体是实现步骤如下:

  1. 创建加载等待环形进度条:
    <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>
     
  2. 创建ListView布局,这里的ListView布局放置在一个FrameLayout里,代码如下:
    <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>
  3. 创建ListView条目布局,代码如下:
    <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>
     
  4. 实现自定的Adapter,详细可以参考自定义Adapter的知识,主要代码如下:
    /**
    * 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;
    }
    
    }
     
  5. 主界面的实现,这里在fragment中实现与activity大致相似,主要是通过ListView的OnScrollListener监听事件来控制LlistView的加载,主要代码如下:
    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) {
    
    26686
    //不处理
    } else {
    loadAll = true;
    }
    //发送消息给主线程
    scrollHandler.sendMessage(scrollHandler.obtainMessage(100, list));
    }
    }).start();
    }
    }
    });
    }
    }
     说明:这里只给出了主要的实现方式,及部分代码,PUser 及 业务处理类就不贴了,当然也不足与待优化的地方。继续学习!

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: