1 ListView概述
下面看一下ListView的应用场景:
2 适配器
先看下适配器的使用步骤:
- 准备布局(每一项的显示效果)
- 准备数据源
- 实例化适配器(布局+数据源)
- 为ListView设置适配器
2.1 ArrayAdapter
效果如下:
代码也十分简单:
package com.example.listviewdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
/**
* 1.准备布局(每一项的显示效果)
* 2.准备数据源
* 3.实例化适配器(布局+数据源)
* 4.为ListView设置适配器
*/
public class ArrayActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_array);
ListView listView1 = findViewById(R.id.list_view1);
//参数1:环境上下文(this)
//参数2:代表数据项所应用的布局
//参数3:数据源(数组)
String[] data = {"AA","BB","CC","DD","EE","FF","GG","AA","BB","CC","DD","EE","FF","GG","AA","BB","CC","DD","EE","FF","GG","AA","BB","CC","DD","EE","FF","GG"};
//使用自定义资源
ArrayAdapter adapter = new ArrayAdapter(this,R.layout.item2,R.id.txt1,data);
listView1.setAdapter(adapter);
}
}
2.2 SimpleAdapter
先看下要实现的效果:
首先我们要自定义列表显示的layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content">
<ImageView
android:id="@+id/qq_img"
android:layout_width="50dp"
android:layout_height="65dp"
android:background="@mipmap/caocao"/>
<TextView
android:id="@+id/qq_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/qq_img"
android:textSize="26sp"
android:text="曹操"/>
<TextView
android:id="@+id/qq_mood"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/qq_img"
android:layout_below="@id/qq_name"
android:textColor="#00ffff"
android:text="测试数据"/>
</RelativeLayout>
然后定义Activity对应的Layout:
<?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"
tools:context=".SimpleActivity">
<ListView
android:id="@+id/list_view2"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</androidx.constraintlayout.widget.ConstraintLayout>
然后看下Java代码:
package com.example.listviewdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SimpleActivity extends AppCompatActivity {
private List<Map<String,Object>> data = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
//获取ListView对象
ListView listView2 = findViewById(R.id.list_view2);
//实例化适配器对象
//参数1:this
//参数2:数据源
initData();
//参数3:每一项布局
//参数4:数据来源的key数组
String[] from = {"img","name","mood"};
//参数5:数据去向的id数组
int[] to = {R.id.qq_img,R.id.qq_name,R.id.qq_mood};
//参数45对应索引上,from数组的元素代码数据源每个map的key,该key所指代的数据
//会作为to数组对应索引上id所代表的控件的内容显示处理
SimpleAdapter adapter = new SimpleAdapter(this,data,R.layout.item3,from,to);
//为ListView设置适配器
listView2.setAdapter(adapter);
//点击事件
listView2.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//用Toast提示Name Mood
Map<String,Object> map = data.get(i);
String name = map.get("name").toString();
String mood = map.get("mood").toString();
Toast.makeText(SimpleActivity.this,name+" "+mood,Toast.LENGTH_SHORT).show();
}
});
}
public void initData(){
//左边:头像 右上:名字 右下:心情
Map<String,Object> map1 = new HashMap<>();
map1.put("img",R.mipmap.caocao);
map1.put("name","曹操");
map1.put("mood","宁教我负天下人,休教天下人负我");
Map<String,Object> map2 = new HashMap<>();
map2.put("img",R.mipmap.zhenji);
map2.put("name","甄姬");
map2.put("mood","飘摇兮若流风之回雪,仿佛兮若轻云之蔽月");
Map<String,Object> map3 = new HashMap<>();
map3.put("img",R.mipmap.simayi);
map3.put("name","司马懿");
map3.put("mood","无奈天命之子");
Map<String,Object> map4 = new HashMap<>();
map4.put("img",R.mipmap.guojia);
map4.put("name","郭嘉");
map4.put("mood","哦");
Map<String,Object> map5 = new HashMap<>();
map5.put("img",R.mipmap.caocao);
map5.put("name","曹操");
map5.put("mood","宁教我负天下人,休教天下人负我");
Map<String,Object> map6 = new HashMap<>();
map6.put("img",R.mipmap.zhenji);
map6.put("name","甄姬");
map6.put("mood","飘摇兮若流风之回雪,仿佛兮若轻云之蔽月");
Map<String,Object> map7 = new HashMap<>();
map7.put("img",R.mipmap.simayi);
map7.put("name","司马懿");
map7.put("mood","无奈天命之子");
Map<String,Object> map8 = new HashMap<>();
map8.put("img",R.mipmap.guojia);
map8.put("name","郭嘉");
map8.put("mood","哦");
data.add(map1);
data.add(map2);
data.add(map3);
data.add(map4);
data.add(map5);
data.add(map6);
data.add(map7);
data.add(map8);
}
}
2.3 BaseAdapter
下面先看下要实现的效果:
首先看下主页面的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".BaseActivity">
<ListView
android:id="@+id/list_view3"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<ImageView
android:id="@+id/write"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@mipmap/write"
android:layout_gravity="right|bottom"
android:layout_margin="40dp"/>
</FrameLayout>
然后看下item的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--头像,昵称,发表时间(静态),发表内容,点赞,评论,转发-->
<ImageView
android:id="@+id/profile"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@mipmap/profile1"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/nickname"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="用户1"
android:textSize="26sp"
android:layout_toRightOf="@id/profile"
android:layout_marginTop="5dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="2048-10-24"
android:textSize="22sp"
android:layout_toRightOf="@id/profile"
android:layout_below="@id/nickname"/>
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="啊,多么有意义的日子呀!!"
android:textSize="22sp"
android:layout_below="@id/profile"/>
<ImageView
android:id="@+id/repost"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/repost"
android:layout_below="@id/content"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"/>
<ImageView
android:id="@+id/comment"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/comment"
android:layout_below="@id/content"
android:layout_toLeftOf="@id/repost"
android:layout_marginRight="10dp"/>
<ImageView
android:id="@+id/like"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/like"
android:layout_below="@id/content"
android:layout_toLeftOf="@id/comment"
android:layout_marginRight="10dp"/>
</RelativeLayout>
这里我们需要定义一个实体类,实体类如下:
package com.example.listviewdemo;
public class Msg {
private int profile;
private String nickname;
private String content;
private boolean isLike;
public int getProfile() {
return profile;
}
public void setProfile(int profile) {
this.profile = profile;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public boolean isLike() {
return isLike;
}
public void setLike(boolean like) {
isLike = like;
}
public Msg(int profile, String nickname, String content, boolean isLike) {
this.profile = profile;
this.nickname = nickname;
this.content = content;
this.isLike = isLike;
}
}
然后我们需要定义自己的Adapter类:
package com.example.listviewdemo;
import android.content.Context;
import android.util.Log;
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 android.widget.Toast;
import java.util.List;
//根据准备好的数据源和子项布局完成ListView效果的一一设置
//做出一些细节处理
public class MyAdapter extends BaseAdapter {
private List<Msg> list;
private Context ctx;
public MyAdapter(List<Msg> list, Context ctx){
this.list = list;
this.ctx = ctx;
}
//获取数量(设置ListView的长度)
@Override
public int getCount() {
return list.size();
}
//获取视图(设置ListView每一项的显示效果)--每个视图出现时都会执行
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
//完成对view的设置
//将布局资源转为View
//参数1:你所要引用的布局资源
//RecycleBin
ViewHolder holder;
if(view == null) {
Log.e("TAG", "======" + i);
//优化1:利用进入RecycleBin中的View,减少读view的赋值 (数据项看不到时相关视图就会进入RecycleBin中)
view = LayoutInflater.from(ctx).inflate(R.layout.item4, null);
holder = new ViewHolder();
holder.profile = view.findViewById(R.id.profile);
holder.nickname = view.findViewById(R.id.nickname);
holder.content = view.findViewById(R.id.content);
holder.like = view.findViewById(R.id.like);
holder.comment = view.findViewById(R.id.comment);
holder.repost = view.findViewById(R.id.repost);
view.setTag(holder);
}else{
//通过getTag()取出ViewHolder对象,然后能过直接通过holder.控件的方式在外面直接操作控件
//从而避免了大幅度使用findViewById操作
//而事实上,getTag()本身操作效率高
holder = (ViewHolder) view.getTag();
}
Msg m = list.get(i);
//头像
// ImageView profile = view.findViewById(R.id.profile);
holder.profile.setImageResource(m.getProfile());
//昵称
// TextView nickname = view.findViewById(R.id.nickname);
holder.nickname.setText(m.getNickname());
//内容
// TextView content = view.findViewById(R.id.content);
holder.content.setText(m.getContent());
//是否点赞
// ImageView like = view.findViewById(R.id.like);
if(m.isLike()){
holder.like.setImageResource(R.mipmap.liked);
}else{
holder.like.setImageResource(R.mipmap.like);
}
//评论
// ImageView comment = view.findViewById(R.id.comment);
holder.comment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(ctx,"你点击了评论",Toast.LENGTH_SHORT).show();
}
});
//转发
// ImageView repost = view.findViewById(R.id.repost);
holder.repost.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(ctx,"--------转发----------",Toast.LENGTH_SHORT).show();
}
});
return view;
}
//==============================================酱油方法
//获取子项
@Override
public Object getItem(int i) {
return null;
}
//获取子项id
@Override
public long getItemId(int i) {
return 0;
}
//1.自定义一个类,叫做ViewHolder
//2.将需要保存的视图声明为公开的属性
//3.什么时候保存?当view为null时,完成对ViewHolder的实例化工作,并为各个控件属性赋值
//4.什么时候用? 什么时候都要用(性能的提升是在view不为null时体现,滚动ListView时体现)
//5.怎么用?当view为null时,完成了ViewHolder及内部控件属性的初始化工作后,调用一句代码
//view.setTag(holder);
//当view不为null时,holder = view.getTag();
static class ViewHolder{
public ImageView profile,like,comment,repost;
public TextView nickname,content;
}
}
接下来看下最后的Activity:
package com.example.listviewdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class BaseActivity extends AppCompatActivity {
private ListView listView3;
private ImageView write;
private List<Msg> list = new ArrayList<>();
private int[] ps = {R.mipmap.profile1,R.mipmap.profile2,
R.mipmap.profile3,R.mipmap.profile4,R.mipmap.profile5,R.mipmap.profile6,
R.mipmap.profile7,R.mipmap.profile8};
private BaseAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
listView3 = findViewById(R.id.list_view3);
write = findViewById(R.id.write);
initData();
//需要传的参数:环境,数据源
adapter = new MyAdapter(list,this);
//设置适配器
listView3.setAdapter(adapter);
write.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Msg m = new Msg(R.mipmap.profile9,"paradox","这是动态新增的说说",false);
list.add(m);
//通知适配器更新数据
adapter.notifyDataSetChanged();
//设置listview自动显示到最新数据
listView3.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
}
});
}
private void initData() {
for(int i = 1 ; i <= 8 ; i++){
Msg m = new Msg(ps[i-1],"用户"+i,"今天天气好晴朗,慕课处处好风光"+i,i%2==0?true:false);
list.add(m);
}
}
}
我们可以看到在ListView中进行了2个优化。ViewHolder通常出现在适配器里,为的是listview滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。