第一步,使用ListView展示数据
1;在Activity中找到ListView控件:listView=(ListView) findViewById(R.id.listView);
2;创建一个适配器:adapter = new MyAdapter(GetNumber.lists,this);
3;给listView设置适配器:listView.setAdapter(adapter);
OK!完成以上三步,就可以用listview加载数据了
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
private ListView listView;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GetNumber.getNumber(this);
listView= (ListView) findViewById(R.id.listView);
adapter = new MyAdapter(GetNumber.lists,this);
listView.setAdapter(adapter);
}
}
第二步,创建ListView的适配器
1:新建一个类MyAdapter继承自BaseAdapter,并实现继承方法
2:写构造方法,以便从适配器调用之avtivity处传递需要的参数,一般有List 《PhoneInfo》 lists, Context context这两种参数;lists是列表对象,PhoneInfo是数据类型;context是上下文;
3:继承方法的复写
@Override
public int getCount() {
return allBalckNum == null ? 0 : allBalckNum.size();
}
@Override
public Object getItem(int position) {
return allBalckNum == null ? 0 :allBalckNum.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
按
return allBalckNum == null ? 0 : allBalckNum.size();
这种格式写,可以避免初始数据没有时,出现空指针异常
getItemId方法没有特别需求时,可以返回position;
4:这继承BaseAdapter中最重要的一个复写方法getView,在此方法中布局个列表项,可实现对列表各子项的操作,实现数据设置和布局;
一般会用下面的一段代码:
//用HoldView来引用要操作的控件,取代findviewbyid
private static class HolderView {
TextView blacknumTv;
TextView modeTv;
ImageView deleteIv;
}
下面贴上MyAdapter.class
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
/**
* Created by zhangtao on 2016/4/14.
*/
public class MyAdapter extends BaseAdapter {
private List<PhoneInfo> lists;
private Context context;
private LinearLayout linearLayout;
public MyAdapter(List<PhoneInfo> lists, Context context) {
this.lists = lists;
this.context = context;
}
@Override
public int getCount() { //返回集合的数量
return lists.size();
}
@Override
public Object getItem(int position) { //返回当前的这条数据
return lists.get(position);
}
@Override
public long getItemId(int position) {
return position; //id和position相等
}
// @Override
// public View getView(int position, View convertView, ViewGroup parent) {
// LayoutInflater inflater=LayoutInflater.from(context );
// linearLayout = (LinearLayout) inflater.inflate(R.layout.call,null);
// ImageView pIv = (ImageView) linearLayout.findViewById(R.id.p);
// TextView nameTv = (TextView) linearLayout.findViewById(R.id.name);
// TextView numberTv = (TextView) linearLayout.findViewById(R.id.number);
// nameTv.setText(lists.get(position).getName());
// numberTv.setText(lists.get(position).getNumber());
// return linearLayout;
// }
@Override
public View getView(int position, View convertView, ViewGroup parent) {
viewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.call, null);
holder = new viewHolder();
holder.nameTv = (TextView) convertView.findViewById(R.id.name);
holder.numberTv = (TextView) convertView.findViewById(R.id.number);
holder.nameTv.setText(lists.get(position).getName());
holder.numberTv.setText(lists.get(position).getNumber());
convertView.setTag(holder);
} else {
holder = (viewHolder) convertView.getTag();
holder.nameTv.setText(lists.get(position).getName());
holder.numberTv.setText(lists.get(position).getNumber());
}
return convertView;
}
private static class viewHolder {
TextView nameTv;
TextView numberTv;
}
}
第三步,PhoneInfo类作用
1;在此类中定义ListView各项用到的数据,以便取得或者设置这些数据;
2;用这些变量,写的get和set方法,本类中的构造方法也是传递参数用的;
下面贴上PhoneInfo.class代码
import android.widget.ImageView;
/**
* Created by zhangtao on 2016/4/14.
*/
public class PhoneInfo {
private String name;
private String number;
public PhoneInfo(String name,String number) {
setName(name);
setNumber(number);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
第四步,获取数据,加载数据
获取数据这一部分有很多种,也是一个重点;可以简单的参照下面的代码;
根据上述写的,向列表中加数据就一个代码:lists.add(phoneInfo );
然后就是获取phoneInfo数据了,这里也不多说上代码;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by zhangtao on 2016/4/13.
*/
public class GetNumber {
public static List<PhoneInfo> lists = new ArrayList<PhoneInfo>();
public static String getNumber(Context conetxt){
Cursor cursor=conetxt.getContentResolver().query(ContactsContract.CommonDataKinds.Phone
.CONTENT_URI,null ,null,null ,null);
String phoneNumber;
String phoneName;
while (cursor.moveToNext()){
phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds
.Phone.NUMBER));
phoneName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds
.Phone.DISPLAY_NAME));
PhoneInfo phoneInfo =new PhoneInfo(phoneName ,phoneNumber );
lists.add(phoneInfo );
System.out.println(phoneName+phoneNumber);
}
return null;
}
}
第五步,分批加载数据—-滑动到最后一个数据时加载数据
此法依赖于可以分批的获取数据
1,用一个方法先在ListView中填充一定数据:inflateData();
2,然后,这里使用上滑到底部加载数据的方式;对ListView做滚动监听,在滚动状态改变方法(onScrollStateChanged)中做操作,其中参数scrollState——滑动状态有三种:SCROLL_STATE_FLING惯性滚动,没有触摸;SCROLL_STATE_IDLE 停滞状态,没有滚动;SCROLL_STATE_TOUCH_SCROLL 触摸滚动;
本监听采用的是停滞状态,并要求加载出来即可见的数据最后一个是本次加载的全部数据的最后一个(即加载的全部数据减一);此时改变偏移量为之前加载数据的总个数;然后再次填充数据;这就要求这次加载的数据保留,而之前加载的数据也能保留;请看第3步inflateData();
//添加滚动监听
mBlackNumLv.setOnScrollListener(new AbsListView.OnScrollListener() {
//滚动状态改变时回调
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//依据滚动状态分批加载数据
if (scrollState == SCROLL_STATE_IDLE) {
int lastVisiblePosition = mBlackNumLv.getLastVisiblePosition();//取得最后可见的列表项的位置
int size=allBalckNum.size();
if (lastVisiblePosition==size-1){
OFFSET = LIMIT+OFFSET;
//分批加载数据
inflateData();
}
}
}
//滚动时回调
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
}
});
}
3,在ListView中填充加载数据;因为要加载数据较多,比较耗时,所以使用开辟子线程使用AsyncTask方法;
分批加载数据,实际加载数据的操作是:
if (allBalckNum==null){
allBalckNum = blackNumDao.getPartBalckNum(LIMIT,OFFSET);
}else {
if (blackNumDao.getPartBalckNum(LIMIT,OFFSET)!=null){
allBalckNum.addAll(blackNumDao.getPartBalckNum(LIMIT,OFFSET));
}
}
如果List中数据为空allBalckNum = blackNumDao.getPartBalckNum(LIMIT,OFFSET);
此方法项里面加载LIMIT 条数据,在getPartBalckNum中使用的是List.add()方法逐条加载数据;
如果List中已有数据,且后续还有未加载的数据(即不为空),则执行allBalckNum.addAll(blackNumDao.getPartBalckNum(LIMIT,OFFSET));
其中List.addAll(),方法是向List中加载一个List集合;
关于List.add和List.addAllde 区别,可以参考add与addAll的区别
下面贴上本部分代码
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.TextView;
import com.example.zhangtao.phonesafe.adapter.BlackNumAdapter;
import com.example.zhangtao.phonesafe.db.dao.BlackNumDao;
import com.example.zhangtao.phonesafe.entity.BlackNumInfo;
import com.example.zhangtao.phonesafe.utils.Canstants;
import com.example.zhangtao.phonesafe.utils.ToastUtils;
import java.util.List;
import java.util.zip.Inflater;
public class CallAndSMSActivity extends AppCompatActivity {
private ListView mBlackNumLv;
private ProgressBar mProgress;
private BlackNumDao blackNumDao;
private List<BlackNumInfo> allBalckNum; //黑名单列表数据
private Context context;
private BlackNumAdapter bnAdapter;
public static final int LIMIT = 20;//查询条数限制
public static int OFFSET = 0;//查询偏移量
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_and_sms);
mBlackNumLv = (ListView) findViewById(R.id.lv_blacknum);
mProgress = (ProgressBar) findViewById(R.id.pb_progress);
context = this;
blackNumDao = new BlackNumDao(this);
/**
*allBalckNum = blackNumDao.getAllBalckNum();
* 填充数据
*/
inflateData();
//添加滚动监听
mBlackNumLv.setOnScrollListener(new AbsListView.OnScrollListener() {
//滚动状态改变时回调
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//依据滚动状态分批加载数据
if (scrollState == SCROLL_STATE_IDLE) {
int lastVisiblePosition = mBlackNumLv.getLastVisiblePosition();//取得最后可见的列表项的位置
int size=allBalckNum.size();
if (lastVisiblePosition==size-1){
OFFSET = LIMIT+OFFSET;
//分批加载数据
inflateData();
}
}
}
//滚动时回调
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
}
});
}
//获取数据库中的数据,是比较耗时的操作,最好开启子线程
private void inflateData() {
new AsyncTask<String, Integer, String>() {
//在执行工作线程之前调用,用于访问工作线程之前的初始化操作
@Override
protected void onPreExecute() {
mProgress.setVisibility(View.VISIBLE);
}
//在后台,在子线程,在工作线程运行
@Override
protected String doInBackground(String... params) {
if (allBalckNum==null){
allBalckNum = blackNumDao.getPartBalckNum(LIMIT,OFFSET);
}else {
if (blackNumDao.getPartBalckNum(LIMIT,OFFSET)!=null){
allBalckNum.addAll(blackNumDao.getPartBalckNum(LIMIT,OFFSET));
}
}
return null;
}
//在工作线程执行完成之后在回调该方法
@Override
protected void onPostExecute(String s) {
mProgress.setVisibility(View.INVISIBLE);
//allBlackNum适配数据
if (bnAdapter == null) {
bnAdapter = new BlackNumAdapter(allBalckNum, context);
mBlackNumLv.setAdapter(bnAdapter);
} else {
bnAdapter.setData(allBalckNum); //重新设置适配器要装配的数据
bnAdapter.notifyDataSetChanged(); //通知数据集发生改变
}
}
}.execute();
}
总结
关于ListView中数据流的走向,以从sqlite中获取数据为例
1获取数据库:SQLiteDatabase db = openHelper.getWritableDatabase();
2获取游标:Cursor cursor = db.query(TABLE, new String[]{NUM, MODE}, null, null, null, null,offset+”,” +
“”+limit);,此处设置获取数据类型,条件,获取条数,偏移量
3逐条读取游标:
while (cursor.moveToNext()) {
String num = cursor.getString(0);
int mode = cursor.getInt(1);
BlackNumInfo blackNumInfo = new BlackNumInfo(num, mode);
data.add(blackNumInfo);
}
1.使用游标分别获取num mode数据;
2.然后讲num mode 注入到BlackNumInfo 作为一组数据;
3.最后用add方法将数据集BlackNumInfo 添加到List中作为List的一个item;
4使用getPartNum每次项List中加载LIMIT条数据;
AsyncTask的使用
适用情况:比较耗时的操作不适合在ui线程进行的操作
使用方法:在方法中执行:new AsyncTask<String, Integer, String>() { }.execute();
在中括号中做操作;
在AsyncTask有三个方法:
1.onPreExecute:在执行工作线程之前调用,用于访问工作线程之前的初始化操作
2.doInBackground:在后台,在子线程,在工作线程运行
3.onPostExecute:在工作线程执行完成之后在回调该方法
代码如下:
//获取数据库中的数据,是比较耗时的操作,最好开启子线程
private void inflateData() {
new AsyncTask<String, Integer, String>() {
//在执行工作线程之前调用,用于访问工作线程之前的初始化操作
@Override
protected void onPreExecute() {
mProgress.setVisibility(View.VISIBLE);
}
//在后台,在子线程,在工作线程运行
@Override
protected String doInBackground(String... params) {
if (allBalckNum==null){
allBalckNum = blackNumDao.getPartBalckNum(LIMIT,OFFSET);
}else {
if (blackNumDao.getPartBalckNum(LIMIT,OFFSET)!=null){
allBalckNum.addAll(blackNumDao.getPartBalckNum(LIMIT,OFFSET));
}
}
return null;
}
//在工作线程执行完成之后在回调该方法
@Override
protected void onPostExecute(String s) {
mProgress.setVisibility(View.INVISIBLE);
//allBlackNum适配数据
if (bnAdapter == null) {
bnAdapter = new BlackNumAdapter(allBalckNum, context);
mBlackNumLv.setAdapter(bnAdapter);
} else {
bnAdapter.setData(allBalckNum); //重新设置适配器要装配的数据
bnAdapter.notifyDataSetChanged(); //通知数据集发生改变
}
}
}.execute();
}