ListView知识回顾,listview控件一个大体的原理是,listview自己界面里面是没有任何东西展现的,它里面的数据的须要一个adapter,adapter能产生数据,而后通过adapter再将数据填充到listview控件里,这就是listview为何能显示内容的缘由。android
问题:咱们在以前写item布局的时候给定了item高度,可是仔细观察发现并无生效,这里咱们换一种方式,match_parent,能够发现也没有生效,无论怎么设置都默认wrap_content,那当咱们怎样才能给这个item设置高度呢?
web
第一个方法,在item里面嵌套一个子布局,设置子布局的高度
第二个方法,无需嵌套(减小布局的加载)面试
咱们能够看到下图item没有嵌套布局,可是高度设置为400dpapp
咱们能够看到item高度400dp生效了
注意inflate方法用第二个ide
activity代码svg
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代码this
public class Chat { public String name; public String content; public String time; }
问题:当咱们向下 滚动listview时,listview往上超出界面的item,还会不会存在内存里,会不会被回收掉?
spa
咱们能够作一个小实验(实验内容是往下滚动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的数量,而且设置列表方向)