编程开发 购物 网址 游戏 小说 歌词 地图 快照 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 开发 租车 短信 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
移动开发 架构设计 编程语言 互联网 开发经验 Web前端 开发总结
开发杂谈 系统运维 研发管理 数据库 云 计 算 Java开发
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
  编程开发知识库 -> 移动开发 -> 【Android UI】ListView的使用和简单优化 -> 正文阅读
 

[移动开发]【Android UI】ListView的使用和简单优化[第1页]


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设计模式之——状态模式
Android解决ScrollView默认不从顶部显示
关于在QtChart中绘出的图形进行点选的问题
Android Studio入门阶段的山路弯弯之Androi
Android设计模式之——责任链模式
DialogFragment实现自定义布局的小技巧
基于局部flush和全屏flush
android 使用binder实现Service与activity进
android性能优化实战理论篇
jenkins+maven+svn实现简单的一键发布
上一篇文章      下一篇文章      查看所有文章
加:2016-07-06 16:27:02  更:2016-07-06 16:28:14 
VC(MFC) Delphi VB C++(C语言) C++ Builder 其它开发语言 云计算 Java开发 .Net开发 IOS开发 Android开发 PHP语言 JavaScript
ASP语言 HTML(CSS) HTML5 Apache MSSQL数据库 Oracle数据库 PowerBuilder Informatica 其它数据库 硬件及嵌入式开发 Linux开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年7日历
2018-7-19 0:18:34
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  编程开发知识库