Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)
- 1.ListView item高度问题
- 2.ListView控件缺点
- 3.ListView控件优化(面试问题)
- 4.ListView控件二次优化
Android 入门第四讲01-列表ListView(用代码添加布局文件(添加控件+布局),ListView原理,ListView使用步骤,ListView填充数据案例,ListView填充多个数据)
ListView知识回顾,listview控件一个大致的原理是,listview本身界面里面是没有任何东西展示的,它里面的数据的需要一个adapter,adapter能产生数据,然后经过adapter再将数据填充到listview控件里,这就是listview为什么能显示内容的原因。
1.ListView item高度问题
问题:我们在之前写item布局的时候给定了item高度,但是仔细观察发现并没有生效,这里我们换一种方式,match_parent,可以发现也没有生效,不管怎么设置都默认wrap_content,那当我们怎样才能给这个item设置高度呢?
第一个方法,在item里面嵌套一个子布局,设置子布局的高度
第二个方法,无需嵌套(减少布局的加载)
我们可以看到下图item没有嵌套布局,但是高度设置为400dp
我们可以看到item高度400dp生效了
注意inflate方法用第二个
activity代码
public class MainActivity extends AppCompatActivity {ListView mListView;List<Chat> list=new ArrayList<>();@Overrideprotected 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{@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic 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\"><ListViewandroid: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\"><TextViewandroid: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\" /><TextViewandroid: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\" /><TextViewandroid: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;}
2.ListView控件缺点
问题:当我们向下 滚动listview时,listview往上超出界面的item,还会不会存在内存里,会不会被回收掉?
我们可以做一个小实验(实验内容是往下滚动listview,内存变化是不变还是增加)
1.观察初始内存
2.向下滚动listview
3.再观察内存发现内存增加了
所以listview第一个缺点就是 item不会被回收,内存会一直增加,这样显然是不行的
3.ListView控件优化(面试问题)
不过我们可以通过优化listview来解决这个问题,众所周知,listview有一个特点,就是item的样式都是一样的
所以我们可以这样,将滚动出的item循环到下一个要加载的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<>();@Overrideprotected 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{@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空@Overridepublic 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上有需要加载更多文本或者图片,那是不是耗时会更多?
4.ListView控件二次优化
问题分析:我们可以发现,主要的耗时操作就是findViewById,那么是不是我们得想一个方法使findViewById耗时更少甚至不执行呢?
解决思路
- 把item中的findViewById控件保存到一个类中
- 需要findViewById的时候直接去类里面拿
思路分析
- 每一个item都需要一个所对应的类–解决方法:每生成一个item随之生成一个所对应的类
- 优化之后,getView()每次都会调用,但是findViewById调用的次数就和一开始在屏幕上显示的item数量一样,不会每次都被调用
代码
public class MainActivity extends AppCompatActivity {ListView mListView;List<Chat> list=new ArrayList<>();@Overrideprotected 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{@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空@Overridepublic 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的数量,并且设置列表方向)