码迷,mamicode.com
首页 > 移动开发 > 详细

【Android UI】ListView的使用和简单优化

时间:2016-07-06 15:07:50      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:

ListView是每个app中都要使用的,所以今天我来总结下ListView的使用和一些简单的优化。
先看下运行效果:
技术分享

一、创建数据库

为了模拟数据,这里将数据保存数据库中,顺便复习一下SQLite的知识,将数据保存到数据库的好处就是很容易模拟网络请求的延迟。

1.创建数据库打开帮助类BlackNumberDBOpenHelper,它继承自SQLiteOpenHelper

package com.yzx.listviewdemo.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class BlackNumberDBOpenHelper extends SQLiteOpenHelper {

    public BlackNumberDBOpenHelper(Context context) {
        super(context, "blacknumber.db", null, 1);
    }

    //数据库第一次创建的时候调用的方法。 适合做数据库表结构的初始化
    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建数据库的表结构  主键_id 自增长  number黑名单号码  mode拦截模式  1电话拦截 2短信拦截 3全部拦截。
        db.execSQL("create table blacknumber (_id integer primary key autoincrement , number varchar(20), mode varchar(2))");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

2.创建类BlackNumberDao 在里面实现增删改查

package com.yzx.listviewdemo.db.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.util.ArrayList;
import java.util.List;

import com.yzx.listviewdemo.db.BlackNumberDBOpenHelper;
import com.yzx.listviewdemo.domain.BlackNumberInfo;

/**
 * 数据库增删改查的工具类
 *
 */
public class BlackNumberDao {
    private BlackNumberDBOpenHelper helper;

    /**
     * 构造方法中完成数据库打开帮助类的初始化
     * @param context
     */
    public BlackNumberDao(Context context) {
        helper = new BlackNumberDBOpenHelper(context);
    }
    /**
     * 添加一条黑名单号码
     * @param number 黑名单号码
     * @param mode 拦截模式  1电话拦截 2短信拦截 3全部拦截。
     */
    public void add(String number,String mode){
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("number", number);
        values.put("mode", mode);
        db.insert("blacknumber", null, values);
        db.close();
    }
    /**
     * 删除一条黑名单号码
     * @param number 黑名单号码
     */
    public void delete(String number){
        SQLiteDatabase db = helper.getWritableDatabase();
        db.delete("blacknumber", "number=?", new String[]{number});
        db.close();
    }
    /**
     * 更改一条黑名单号码的拦截模式
     * @param number 要修改的黑名单号码
     * @param newmode 新的拦截模式
     */
    public void update(String number, String newmode){
        SQLiteDatabase db = helper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("mode", newmode);
        db.update("blacknumber", values, "number=?", new String[]{number});
        db.close();
    }

    /**
     * 获取全部的黑名单号码信息。
     * @return
     */
    public List<BlackNumberInfo> findAll(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query("blacknumber", new String[]{"number","mode"}, null, null, null, null, null);
        List<BlackNumberInfo>  infos = new ArrayList<BlackNumberInfo>();
        while(cursor.moveToNext()){
            BlackNumberInfo info = new BlackNumberInfo();
            String number = cursor.getString(0);
            String mode = cursor.getString(1);
            info.setMode(mode);
            info.setNumber(number);
            infos.add(info);
        }
        cursor.close();
        db.close();
        return infos;
    }
    /**
     * 分页获取部分的黑名单号码信息。
     * @param startIndex 查询的开始位置
     * @return
     */
    public List<BlackNumberInfo> findPart(int startIndex){
        try {
            Thread.sleep(600);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select number,mode  from blacknumber order by _id desc limit 20 offset ?", new String[]{String.valueOf(startIndex)});
        List<BlackNumberInfo>  infos = new ArrayList<BlackNumberInfo>();
        while(cursor.moveToNext()){
            BlackNumberInfo info = new BlackNumberInfo();
            String number = cursor.getString(0);
            String mode = cursor.getString(1);
            info.setMode(mode);
            info.setNumber(number);
            infos.add(info);
        }
        cursor.close();
        db.close();
        return infos;
    }
    /**
     * 获取数据库一共有多少条记录
     * @return  int 总条目个数
     */
    public int getTotalCount(){
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select count(*) from blacknumber ",null);
        cursor.moveToNext();
        int count = cursor.getInt(0);
        cursor.close();
        db.close();
        return count;
    }
    /**
     * 查询黑名单号码是否存在
     * @param number
     * @return
     */
    public boolean find(String number){
        boolean result = false;
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query("blacknumber", null, "number=?", new String[]{number}, null, null, null);
        if(cursor.moveToNext()){
            result = true;
        }
        cursor.close();
        db.close();
        return result;
    }
    /**
     * 查询黑名单号码的拦截模式
     * @param number
     * @return  null代表不存在  1电话 2短信 3全部
     */
    public String findMode(String number){
        String mode = null;
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query("blacknumber", new String[]{"mode"}, "number=?", new String[]{number}, null, null, null);
        if(cursor.moveToNext()){
            mode = cursor.getString(0);
        }
        cursor.close();
        db.close();
        return mode;
    }
}

3.创建BlackNumberInfo实体

package com.yzx.listviewdemo.domain;

/**
 * Created by yzx on 2016/7/5.
 */
public class BlackNumberInfo {

    private String number;
    private String mode;
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    public String getMode() {
        return mode;
    }
    public void setMode(String mode) {
        this.mode = mode;
    }
    @Override
    public String toString() {
        return "BlackNumberInfo [number=" + number + ", mode=" + mode + "]";
    }


}

里面的Thread.sleep(600)和Thread.sleep(3000)是用来模拟请求网络时的延迟。

二、在使用ListView展示列表

1.便于展示先添加100条数据

Random random = new Random();
//13512340001
for(int i = 0 ;i< 100;i++ ){
 dao.add("1351234000"+i,String.valueOf(random.nextInt(3)+1));
}

2.列表的展示
在onCreate中初始化数据和布局文件,只有自定义继承自BaseAdapter的Adapter,在其getView加在每个item的布局文件。这样就能展示数据了,但是存在很多的问题,下面我们就来简单的优化。

三、ListView的简单优化

1.使用历史缓存的显示数据

View view;
if(convertView != null){
    view = convertView;
    Log.i(TAG, "使用历史缓存的显示数据"+position);
}else{
    Log.i(TAG, "创建新的View的显示数据"+position);
    view=View.inflate(CallSmsSafeActivity.this,
                 R.layout.list_callsmssafe_item, null);
}

2.使用ViewHolder优化
因为寻找孩子的过程是一个比较耗时,消耗资源的操作,进一步的优化。没有必要每一次都去查看每个孩子的特征,根据id得到孩子的引用。只需要在孩子出生的时候,找到特征,把特征给存起来。这里可以使用ViewHolder作为容器来保存孩子的引用。

private class CallSmsSafeAdapter extends BaseAdapter {
    private static final String TAG = "CallSmsSafeAdapter";

    @Override
    public int getCount() {
        return infos.size();
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // 为了减少view对象创建的个数 使用历史缓存的view对象 convertview
        View view;
        ViewHolder holder;
        if (convertView != null) {
            view = convertView;
            Log.i(TAG, "使用历史缓存的view对象" + position);
            holder = (ViewHolder) view.getTag();
        } else {
            Log.i(TAG, "创建新的view对象" + position);
            view = View.inflate(getApplicationContext(),
                    R.layout.list_callsmssafe_item, null);
            // 寻找孩子的过程 是一个比较耗时 消耗资源的操作 进一步的优化。
            // 没有必要每一次都去查看每个孩子的特征 根据id得到孩子的引用。
            // 只需要在孩子出生的时候 , 找到特征 ,把特征给存起来
            holder = new ViewHolder();// 买了一个记事本 记录孩子的信息。
            holder.tv_number = (TextView) view.findViewById(R.id.tv_number);
            holder.tv_mode = (TextView) view.findViewById(R.id.tv_mode);
            holder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
                view.setTag(holder); // 把孩子的引用 记事本 放在口袋里
        }

        BlackNumberInfo info = infos.get(position);
        String mode = info.getMode();
        if ("1".equals(mode)) {
            holder.tv_mode.setText("电话拦截");
        } else if ("2".equals(mode)) {
            holder.tv_mode.setText("短信拦截");
        } else if ("3".equals(mode)) {
            holder.tv_mode.setText("电话+短信拦截");
        }
        holder.tv_number.setText(info.getNumber());

        holder.iv_delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                BlackNumberInfo info = infos.get(position);
                String number = info.getNumber();
                dao.delete(number);//删除数据库里面的记录。
                infos.remove(info);//删除当前界面对应的集合的数据。
                adapter.notifyDataSetChanged();//通知界面刷新。
                totalCount--;
            }
        });

        return view;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }
}

class ViewHolder {
    TextView tv_number;
    TextView tv_mode;
    ImageView iv_delete;
}

四、ListView的UI效果优化

因为我们我们在app中使用ListView展示数据一般都是请求网络获取数据的,所以曾在延迟,我们应该给用户显示一个ProgressBar来增加用户的等待欲。当数据很多时,我们不仅要使用ProgressBar,还要分批加载数据。
1.创建一个线程去读取数据

new Thread() {
  public void run() {
    list = dao.findAll();//查询数据库得到数据
    handler.sendEmptyMessage(0);//发消息给主线程更新数据
  };
}.start();

//更新数据
private Handler handler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        lv_call_sms_safe.setAdapter(new CallSmsSafeAdapter());
    };
};

2.没数据的时候加一个提醒效果
在布局文件中添加相应控件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <ListView
            android:id="@+id/lv_call_sms_safe"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </ListView>

        <LinearLayout
            android:id="@+id/ll_loading"
            android:visibility="invisible"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical" >

            <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="玩命加载中。。。" />
        </LinearLayout>
    </FrameLayout>

</LinearLayout>
//代码配合处理
ll_loading.setVisibility(View.VISIBLE);//没数据的时候显示
ll_loading.setVisibility(View.INVISIBLE);//数据加载好了隐藏

3.分批加载的好处:不用等待太久、节约流量、慢慢引导用户看感兴趣内容

/**
 * 分页获取部分的黑名单号码信息。
 * @param startIndex 查询的开始位置
 * @return
 */
public List<BlackNumberInfo> findPart(int startIndex){
    try {
        Thread.sleep(600);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    SQLiteDatabase db = helper.getReadableDatabase();
    Cursor cursor = db.rawQuery("select number,mode  from blacknumber order by _id desc limit 20 offset ?", new String[]{String.valueOf(startIndex)});
    List<BlackNumberInfo>  infos = new ArrayList<BlackNumberInfo>();
    while(cursor.moveToNext()){
        BlackNumberInfo info = new BlackNumberInfo();
        String number = cursor.getString(0);
        String mode = cursor.getString(1);
        info.setMode(mode);
        info.setNumber(number);
        infos.add(info);
    }
    cursor.close();
    db.close();
    return infos;
}

4.监听拖动到末尾

lv_call_sms_safe.setOnScrollListener(new AbsListView.OnScrollListener() {
    // 滚动状态变化了。
    // 静止-->滚动
    // 滚动-->静止
    // 手指触摸滚动-->惯性滑动
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:// 静止状态
                // 判断界面是否已经滑动到了最后面。
                // 获取最后一个可见条目的位置
                int position = lv_call_sms_safe.getLastVisiblePosition(); // 19
                int total = infos.size(); // 20
                if (isloading) {
                    Toast.makeText(getApplicationContext(),
                            "正在加载数据,不要重复刷新。", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (position >= (total - 5)) {

                    // 指定新的获取数据的开始位置
                    startIndex += 20;
                    if (startIndex >= totalCount) {
                        Toast.makeText(getApplicationContext(),
                                "没有更多数据了,别再拖了", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    Toast.makeText(getApplicationContext(),
                            "拖动到最下面了。加载更多数据", Toast.LENGTH_SHORT).show();
                    fillData();
                }
                break;
            case AbsListView.OnScrollListener.SCROLL_STATE_FLING:// 惯性滑动

                break;
            case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:// 触摸滚动

                break;
        }

    }

    // 滚动的时候执行的方法
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {

    }
});
private void fillData() {
    ll_loading.setVisibility(View.VISIBLE);
    isloading = true;
    new Thread() {
        public void run() {
            if (infos == null) {
                infos = dao.findPart(startIndex);
            } else {// 原来集合里面已经有数据了。
                infos.addAll(dao.findPart(startIndex));
            }
            // 发送一个消息 更新界面。
            handler.sendEmptyMessage(0);
        };
    }.start();
}

5.让数据继续停留在当前位置
两种实现方式
第一种:lv_call_sms_safe.setSelection(startIndex);//不推荐
第二种:重复利用适配器,数据变化通知一下就行了
private CallSmsSafeAdapter adapter;

Handler里面修改成:

if(adapter == null){
    adapter = new CallSmsSafeAdapter();
    lv_call_sms_safe.setAdapter(adapter);
}else{
    //通知数据适配器更新一下界面
    adapter.notifyDataSetChanged();
}

6.防止重复加载
定义成员变量

private boolean isLoading  = false;

在加载的过程中

if(isLoading){
    return;
} 

isLoading = true;

加载好后 handler里面处理

isLoading = false;  

7.处理拖动到所有数据的最后一条时的处理
得到数据库一共有多少条数据

 /**
 * 得到数据的总数
 * @return
 */
public int  getTotalCount(){
    int count = 0;
    SQLiteDatabase database = helper.getWritableDatabase();
    Cursor cursor = database.rawQuery("select count(*) from blacknumber", null);
    if(cursor.moveToNext()){
         count = cursor.getInt(0);
    }
    cursor.close();
    database.close();
    return count;
}

代码处理

if(startIndex >= total){
    Toast.makeText(getApplicationContext(), "已经最后一条了", 0).show();
    return;
    }

到此就已经实现了ListView的使用和简单优化。

【Android UI】ListView的使用和简单优化

标签:

原文地址:http://blog.csdn.net/yabg_zhi_xiang/article/details/51834906

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!