ListView知识回顾,listview控件一个大致的原理是,listview本身界面里面是没有任何东西展示的,它里面的数据的需要一个adapter,adapter能产生数据,然后经过adapter再将数据填充到listview控件里,这就是listview为什么能显示内容的原因。
问题:我们在之前写item布局的时候给定了item高度,但是仔细观察发现并没有生效,这里我们换一种方式,match_parent,可以发现也没有生效,不管怎么设置都默认wrap_content,那当我们怎样才能给这个item设置高度呢?
第一个方法,在item里面嵌套一个子布局,设置子布局的高度
第二个方法,无需嵌套(减少布局的加载)
我们可以看到下图item没有嵌套布局,但是高度设置为400dp
我们可以看到item高度400dp生效了
注意inflate方法用第二个
public class MainActivity extends AppCompatActivity {
ListView mListView;
List<Chat> list=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView=findViewById(R.id.listview);//实例化
for (int i=0;i<100;i++){
Chat chat=new Chat();
chat.name="name :"+ i;
chat.content="content :"+ i;
chat.time="time :"+ i;
list.add(chat);
}
mListView.setAdapter(new MyAdapter());
}
public class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Chat chat=list.get(position);
// View view=View.inflate(MainActivity.this,R.layout.item,null);
View view= LayoutInflater.from(MainActivity.this).inflate(R.layout.item,parent,false);//使返回的布局高度生效
TextView textView=view.findViewById(R.id.textView);
TextView textView1=view.findViewById(R.id.textView2);
TextView textView2=view.findViewById(R.id.textView3);
textView.setText(chat.name);
textView1.setText(chat.content);
textView2.setText(chat.time);
return view;
}
}
}
activity布局文件代码
<?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=".MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
item 代码
<?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="400dp">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="42dp"
android:layout_marginLeft="42dp"
android:layout_marginTop="25dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="56dp"
android:layout_marginLeft="56dp"
android:layout_marginTop="58dp"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:layout_marginEnd="26dp"
android:layout_marginRight="26dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
实体类Chat代码
public class Chat {
public String name;
public String content;
public String time;
}
我们可以做一个小实验(实验内容是往下滚动listview,内存变化是不变还是增加)
1.观察初始内存
2.向下滚动listview
3.再观察内存发现内存增加了
所以listview第一个缺点就是 item不会被回收,内存会一直增加,这样显然是不行的
不过我们可以通过优化listview来解决这个问题,众所周知,listview有一个特点,就是item的样式都是一样的
所以我们可以这样,将滚动出的item循环到下一个要加载的item
//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空
所以我们可以看到,item没有超出屏幕convertView 返回空值,那么是不是可以用if语句来判断,只需要在convertView 为空值的时候生成item条目就可以了,而当convertView 不等于空时,就不需要new一个条目了,由此来节省内存
优化以后(我们可以发现,向下滚动屏幕,item在变化,但是内存没有增加,问题成功优化)
activity代码(主要原理就是,当convertView 不为空,不会产生新的条目,屏幕上始终是一开始生成的那几个条目)
public class MainActivity extends AppCompatActivity {
ListView mListView;
List<Chat> list=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView=findViewById(R.id.listview);//实例化
for (int i=0;i<100;i++){
Chat chat=new Chat();
chat.name="name :"+ i;
chat.content="content :"+ i;
chat.time="time :"+ i;
list.add(chat);
}
mListView.setAdapter(new MyAdapter());
}
public class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.i("item","convertView :" + convertView);
Chat chat=list.get(position);
// View view=View.inflate(MainActivity.this,R.layout.item,null);
//加载一个布局
if (convertView==null){
convertView= LayoutInflater.from(MainActivity.this).inflate(R.layout.item,parent,false);//使返回的布局生效
}
TextView textView=convertView.findViewById(R.id.textView);
TextView textView1=convertView.findViewById(R.id.textView2);
TextView textView2=convertView.findViewById(R.id.textView3);
textView.setText(chat.name);
textView1.setText(chat.content);
textView2.setText(chat.time);
return convertView;
}
}
}
*但是我们发现,每次生成一个item,getView()方法就会被调用一次,在我们的案例中,getView()方法下调用了三次findViewById的方法,假设findViewById每次调用所需时间为30ms,那么如果一秒钟滚动生成20次item,是不是所需时间为30ms * 3 20=1800ms=1.8s >1s,这种情况就会出现一个卡顿,加载延时,如果item上有需要加载更多文本或者图片,那是不是耗时会更多?
问题分析:我们可以发现,主要的耗时操作就是findViewById,那么是不是我们得想一个方法使findViewById耗时更少甚至不执行呢?
解决思路
思路分析
public class MainActivity extends AppCompatActivity {
ListView mListView;
List<Chat> list=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView=findViewById(R.id.listview);//实例化
for (int i=0;i<100;i++){
Chat chat=new Chat();
chat.name="name :"+ i;
chat.content="content :"+ i;
chat.time="time :"+ i;
list.add(chat);
}
mListView.setAdapter(new MyAdapter());
}
public class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.i("item","convertView :" + convertView);
Chat chat=list.get(position);
// View view=View.inflate(MainActivity.this,R.layout.item,null);
//加载一个布局
MyViewHolder myViewHolder=null;
if (convertView==null){
convertView= LayoutInflater.from(MainActivity.this).inflate(R.layout.item,parent,false);//使返回的布局生效
myViewHolder=new MyViewHolder();
myViewHolder.textView=convertView.findViewById(R.id.textView);//保存到类
myViewHolder.textView1=convertView.findViewById(R.id.textView2);//保存到类
myViewHolder.textView2=convertView.findViewById(R.id.textView3);//保存到类
convertView.setTag(myViewHolder);//MyViewHolder作为convertView的一个成员变量
}else {
myViewHolder= (MyViewHolder) convertView.getTag();//与convertView形成绑定关系,把作为convertView的成员变量(MyViewHolder)取出来
}
//
myViewHolder.textView.setText(chat.name);
myViewHolder.textView1.setText(chat.content);
myViewHolder.textView2.setText(chat.time);
return convertView;
}
}
public static class MyViewHolder{
TextView textView;//成员变量
TextView textView1;
TextView textView2;
}
}
讲到这,关于ListView的重要知识就讲完啦,当然ListView还有更多的奥秘就需要小伙伴你去深度挖掘啦,谢谢您的阅读,下一讲我们将ListView的升级版 RecyclerView.
Android 入门第四讲03-列表RecyclerView(RecyclerView使用步骤(详),RecyclerView指定一行item的数目+指定一行item的数量,并且设置列表方向)