ListView简介

ListView介绍

在Android开发中,ListView是一个比较常用的控件。它以列表的形式展示具体数据内容,并且能够根据数据的长度自适应屏幕显示。

ListView控件的使用

  • 新建Android工程。

  • 在activity_main.xml中加入ListView控件

1
2
3
4
5
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>

常用数据适配器(Adapter)

在使用ListView时需要对其进行数据适配。为了实现这个功能,Android系统提供一系列的适配器(Adapter)对ListView进行数据适配。

适配器就像显示器,把复杂的数据按人们易于接受的方式来展示。

  1. BaseAdapter

    即基本的适配器。它实际上就是一个抽象类,该类拥有四个抽象方法。在Android开发中,就是根据这几个抽象方法来对ListView进行数据适配的

  2. SimpleAdapter

    SimpleAdapter继承自BaseAdapter,实现了BaseAdapter的四个抽象方法,分别是getCount()、getItem()、getItemId()、getView()方法。

  3. ArrayAdapter

    ArrayAdapter也继承自BaseAdapter,与SimpleAdapter相同。ArrayAdapter通常用于适配TextView控件,例如Android系统中的设置菜单(Setting)。

BaseAdapter适配器

  • BaseAdapter是使用得比较多,适用于ListView、GridView、Spinner等控件适配器,通过重写getView方法,展示自定义视图。

  • 掌握BaseAdater的四个抽象方法:getItem(int),getItemId(int),getCount(),getView(int,View,ViewGroup)

  • 使用convertView,ViewHolder 优化ListView

案例: 个人通讯录列表

  1. 自定义布局,用来显示listView的列表单项。

  2. 自定义BaseAdapter适配器。

  3. 绑定绑定数据适配器

  4. 运行通讯录

UI布局设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/item_img"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_weight="1"
app:srcCompat="@android:drawable/btn_star_big_on" />

<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
</LinearLayout>

自定义适配器BaseAdapter

a. 自定义model类Person(数据对象)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.timeemail.entity;

public class Person {

private String name;
private int img;

public Person() {
}

public Person(String name, int img) {
this.name = name;
this.img = img;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getImg() {
return img;
}

public void setImg(int img) {
this.img = img;
}

}

b. 自定义PersonAdapter适配器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.example.timeemail.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.timeemail.R;
import com.example.timeemail.entity.Person;

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

public class PersonAdapter extends BaseAdapter {

private List<Person> pdata = new ArrayList<Person>();

private Context context;

public PersonAdapter(List<Person> pdata, Context context) {
this.pdata = pdata;
this.context = context;
}

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

@Override
public Object getItem(int position) {
return null;
}

@Override
public long getItemId(int position) {
return position;
}

//优化ListView
//定义一个ViewHolder静态类
static class ViewHolder{
//定义属性,对应是列表项数据
public ImageView myimg;
public TextView myname;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

//定义ViewHolder对象
ViewHolder holder;
//判断convertView是否为空,convertView对应的列表项
if (convertView==null){
//新建
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.driftitem,parent,false);
holder.myimg = (ImageView) convertView.findViewById(R.id.item_img);
holder.myname = (TextView) convertView.findViewById(R.id.item_name);

convertView.setTag(holder);

}else{
//复用列表项
holder = (ViewHolder) convertView.getTag();

}
//设置列表项数据
holder.myimg.setImageResource(pdata.get(position).getImg());
holder.myname.setText(pdata.get(position).getName());

return convertView;
}
}

绑定数据适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.example.timeemail;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.example.timeemail.adapter.PersonAdapter;
import com.example.timeemail.entity.Person;

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

public class DriftActivity extends AppCompatActivity {

private ListView lvdrift;

private PersonAdapter myAdapter;

private String[] names = {"李铭","小花","宋妈","张明","姥爷","表哥"};

private int[] imgs = {R.mipmap.tx1,R.mipmap.tx2,R.mipmap.tx3,
R.mipmap.tx4,R.mipmap.tx5,R.mipmap.tx6};

private List<Person> drift = new ArrayList<Person>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drift);

//显示ListView
//1.获取ListView数据
lvdrift = (ListView)findViewById(R.id.lv_drift);
//初始化数据
initDataDrifts();
//2.创建适配器对象(参数1:数据源,参数2:context 上下文)
myAdapter = new PersonAdapter(drift,this);
//3.加载适配器
lvdrift.setAdapter(myAdapter);
}

//初始化数据
private void initDataDrifts(){
for (int i=0;i<names.length;i++){
//新建Person对象,存放头像及姓名
Person person = new Person(names[i],imgs[i]);
//将数据存入数据列表
drift.add(person);
}
}
}

ListView的使用

ListView的常用监听事件

OnItemClickListener 处理视图中单个条目的点击事件

OnItemLongClickListener 处理视图中单个条目的长按事件

OnScrollListener 监视滚动的变换,常用于视图在滚动中加载数据

为通讯录加入点击事件

实现OnItemClickListener监听。

1
public class DriftActivity extends AppCompatActivity implements AdapterView.OnItemClickListener

实现onItemClick方法。

1
2
3
4
5
6
7
8
9
//实现点击方法
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//获取点击对象的条目
Person person = drift.get(position);
//弹出点击通讯录人的姓名
Toast.makeText(this,"您点击了第"+position+
"条通讯录,姓名:"+person.getName(),Toast.LENGTH_LONG).show();
}

为通讯录加入滚动事件

实现OnScrollListener 监听。

1
public class DriftActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, AbsListView.OnScrollListener

实现onScroll,onScrollStateChanged方法。

滚动停止后,增加一条通讯录数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//实现滚动方法
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//判断滚动状态
if(scrollState == SCROLL_STATE_FLING) {
Toast.makeText(this, "手指离开屏幕,列表由于惯性继续滑动", Toast.LENGTH_SHORT).show();
} else if(scrollState == SCROLL_STATE_IDLE){
Toast.makeText(this, "列表停止滑动", Toast.LENGTH_SHORT).show();

Person person = new Person("欣欣",R.mipmap.tx1);
drift.add(person);

//通知ListView刷新界面
myAdapter.notifyDataSetChanged();

} else if(scrollState == SCROLL_STATE_TOUCH_SCROLL){
Toast.makeText(this, "手指处在屏幕,列表正在滑动", Toast.LENGTH_SHORT).show();
}
}

@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

}

RecyclerView简介

RecyclerView介绍

RecyclerView 是一个增强版的ListView

Android 5.0推出的,是support-v7包中的新组件(最低兼容到android 3.0版本)。

RecyclerView,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。

RecyclerView相对于ListView的优点

1、可以使用布局管理器LayoutManager来管理RecyclerView的显示方式:水平、垂直、网络、网格交错布局;

2、自定义item的分割条,实现自定义;

3、可以控制item的添加和删除的动画,非常自由,可以自定义动画,配合具体场景,效果非常棒;

4、可以动态的在指定位置添加和删除某一项,而列表不会回到顶部,动态的update列表数据;

5、缺点:就是没有OnItemClickListenter(),需要自己在RecycleView内部自定义列表项的点击事件或则长按事件;

6、在Material Design中和CardView配合使用,显示效果非常突出。

案例: 个人通讯录列表(二)

  1. 自定义布局,用RecyclerView显示通讯录。

  2. 通过RecyclerView.Adapter适配器。

  3. RecyclerView绑定绑定数据适配器

  4. 运行通讯录

通讯录UI布局设计

a. 定义主布局文件,采用RecyclerView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/register_bg"
tools:context=".DriftActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDrift"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

b. 复用布局文件Driftitem.xml,图文结构显示。

自定义适配器RecyclerView.Adapter

a. 复用model类Person(数据对象)。

b. 自定义RecyclerView.Adapter适配器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.example.timeemail.adapter;

import android.content.Context;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.example.timeemail.R;
import com.example.timeemail.entity.Person;

import org.w3c.dom.Text;

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

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder>{
private List<Person> pdata = new ArrayList<Person>();

private Context context;

public MyRecyclerAdapter(List<Person> pdata, Context context) {
this.pdata = pdata;
this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//填充布局,获取列表项布局文件
View itemview = LayoutInflater.from(context).inflate(R.layout.driftitem,parent,false);
MyViewHolder myViewHolder = new MyViewHolder(itemview);
return myViewHolder;
}

@Override
//填充onCreateViewHolder方法返回的holder控件
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Person person = pdata.get(position);
holder.myimg.setImageResource(person.getImg());
holder.myname.setText(person.getName());
}

@Override
//返回通讯录数据个数
public int getItemCount() {
return pdata.size();
}

class MyViewHolder extends RecyclerView.ViewHolder{
//定义对应列表项
private ImageView myimg;
private TextView myname;
//构造方法
public MyViewHolder(@NonNull View itemView) {
super(itemView);
myimg = (ImageView)itemView.findViewById(R.id.item_img);
myname = (TextView) itemView.findViewById(R.id.item_name);
}
}
}

RecyclerView绑定数据适配器

a. RecyclerView绑定绑定数据适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.example.timeemail;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.example.timeemail.adapter.MyRecyclerAdapter;
import com.example.timeemail.adapter.PersonAdapter;
import com.example.timeemail.entity.Person;

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

public class DriftActivity extends AppCompatActivity {

private RecyclerView Rvdrift;

private MyRecyclerAdapter myAdapter;

private String[] names = {"李铭","小花","宋妈","张明","姥爷","表哥"};

private int[] imgs = {R.mipmap.tx1,R.mipmap.tx2,R.mipmap.tx3,
R.mipmap.tx4,R.mipmap.tx5,R.mipmap.tx6};

private List<Person> drift = new ArrayList<Person>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drift);

//初始化数据
initDataBooks();
//1.初始化控件
Rvdrift = findViewById(R.id.rvDrift);
//2.设置RecyclerView布局管理器
Rvdrift.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
//3.初始化数据适配器
myAdapter = new MyRecyclerAdapter(drift,this);
//4.采用默认动画效果
Rvdrift.setItemAnimator(new DefaultItemAnimator());
//5.设置适配器
Rvdrift.setAdapter(myAdapter);
}

//初始化数据
private void initDataBooks(){
for (int i=0;i<names.length;i++){
//新建Person对象,存放头像及姓名
Person person = new Person(names[i],imgs[i]);
//将数据存入数据列表
drift.add(person);
}
}

}

RecyclerView的使用

RecyclerView的OnItemClickListener监听器

没有提供setOnItemClickListener这个回调,也就是无法响应点击事件。

  1. 定义监听器OnItemClickListener
  2. Adapter中实现监听器OnItemClickListener
  3. 绑定监听器

案例:可点击的个人通讯录列表(三)

  1. 创建点回调接口:OnItemClickListener。

    1
    2
    3
    4
    5
    6
    7
    8
    package com.example.timeemail.adapter;

    import android.view.View;

    public interface OnItemClickListener {

    public void OnItemClick(View view,int postion);
    }
  1. Adapter里设置回调接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //定义监听
    private OnItemClickListener monItemClickListener;

    public void setMonItemClickListener(OnItemClickListener monItemClickListener) {
    this.monItemClickListener = monItemClickListener;
    }
    @Override
    //填充onCreateViewHolder方法返回的holder控件
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
    Person person = pdata.get(position);
    holder.myimg.setImageResource(person.getImg());
    holder.myname.setText(person.getName());
    //设置监听方法
    if(monItemClickListener != null){
    holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    monItemClickListener.OnItemClick(holder.itemView,position);
    }
    });
    }
    }
  2. Activity使用点击回调接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //6.设置监听器
    myAdapter.setMonItemClickListener(new OnItemClickListener() {
    @Override
    public void OnItemClick(View view, int postion) {
    //获取调集通讯录的对象
    Person person = drift.get(postion);
    //弹出提示
    Toast.makeText(DriftActivity.this,"您点击了第个通讯录,姓名为"+person.getName(),Toast.LENGTH_LONG).show();
    }
    });
  3. 运行通讯录