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

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设置高度呢?
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第1张图片

第一个方法,在item里面嵌套一个子布局,设置子布局的高度
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第2张图片
第二个方法,无需嵌套(减少布局的加载)

我们可以看到下图item没有嵌套布局,但是高度设置为400dp

Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第3张图片
我们可以看到item高度400dp生效了
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第4张图片
注意inflate方法用第二个

Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第5张图片
activity代码


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;
}

2.ListView控件缺点

问题:当我们向下 滚动listview时,listview往上超出界面的item,还会不会存在内存里,会不会被回收掉?
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第6张图片

我们可以做一个小实验(实验内容是往下滚动listview,内存变化是不变还是增加)
1.观察初始内存
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第7张图片
2.向下滚动listview

3.再观察内存发现内存增加了
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第8张图片
所以listview第一个缺点就是 item不会被回收,内存会一直增加,这样显然是不行的

3.ListView控件优化(面试问题)

不过我们可以通过优化listview来解决这个问题,众所周知,listview有一个特点,就是item的样式都是一样的

所以我们可以这样,将滚动出的item循环到下一个要加载的item
Android 入门第四讲02-列表ListView(ListView item高度问题,ListView控件缺点,ListView控件优化(面试常问问题),ListView控件二次优化)_第9张图片

  • 听起来好像很简单,但我们需要怎么实现呢?
  • 第一步我们要获取超出屏幕且不能回收的item的内存地址
  • 所以我们先来看一下滑动时超出屏幕的item的地址变化

//convertView 表示系统中有没有可以回收的item,如果有那么会返回内存地址,如果没有返回空

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

所以我们可以看到,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数量一样,不会每次都被调用

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


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

你可能感兴趣的:(Android,入门(全))