Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)

Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)


Android 入门第四讲01-列表ListView(用代码添加布局文件(添加控件+布局),ListView原理,ListView使用步骤,ListView填充数据案例,ListView填充多个数据)

ListView知识回顾,listview控件一个大体的原理是,listview自己界面里面是没有任何东西展现的,它里面的数据的须要一个adapter,adapter能产生数据,而后通过adapter再将数据填充到listview控件里,这就是listview为何能显示内容的缘由。android

1.ListView item高度问题

问题:咱们在以前写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;
}

2.ListView控件缺点

问题:当咱们向下 滚动listview时,listview往上超出界面的item,还会不会存在内存里,会不会被回收掉?
在这里插入图片描述spa

咱们能够作一个小实验(实验内容是往下滚动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<>();
    @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上有须要加载更多文本或者图片,那是否是耗时会更多?

4.ListView控件二次优化

问题分析:咱们能够发现,主要的耗时操做就是findViewById,那么是否是咱们得想一个方法使findViewById耗时更少甚至不执行呢?

解决思路

  • 把item中的findViewById控件保存到一个类中
  • 须要findViewById的时候直接去类里面拿

思路分析

  • 每个item都须要一个所对应的类–解决方法:每生成一个item随之生成一个所对应的类
  • 优化以后,getView()每次都会调用,可是findViewById调用的次数就和一开始在屏幕上显示的item数量同样,不会每次都被调用

在这里插入图片描述
代码

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的数量,而且设置列表方向)