Android学习笔记:ListView

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/MaybeForever/article/details/95998029

代码地址:https://github.com/ambition-hb/ListViewDemo

ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕内的数据则会滚动出屏幕。


一、基本用法

(1)由于数组中的数据无法直接传递给ListView,我们需要借助适配器(Adapter)来完成。
(2)ArrayAdapter可以通过泛型来指定要适配的数据,然后在构造函数中把要适配的数据传入。
(3)android.R.layout.simple_list_item_1是ListView内置的一个子项布局,里面只有一个TextView,可显示一段文本
MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity {

    private String[] data = {"Sunny","Cloudy","Few Clouds","Partly Cloudy","Overcast","Windy","Calm","Light Breeze",
            "Moderate","Fresh Breeze","Strong Breeze","High Wind","Gale","Strong Gale","Storm","Violent Storm","Hurricane",
            "Tornado","Tropical Storm","Shower Rain","Heavy Shower Rain","Thundershower","Heavy Thunderstorm","Thundershower with hail",
            "Light Rain","Moderate Rain","Heavy Rain","Extreme Rain","Drizzle Rain","Storm","Heavy Storm","Severe Storm","Freezing Rain",
            "Light to moderate rain","Moderate to heavy rain","Heavy rain to storm","Storm to heavy storm","Heavy to severe storm",
            "Rain","Light Snow","Moderate Snow","Heavy Snow","Snowstorm","Sleet","Rain And Snow","Shower Snow","Snow Flurry",
            "Light to moderate snow","Moderate to heavy snow","Heavy snow to snowstorm","Snow","Mist","Foggy","Haze","Sand","Dust",
            "Duststorm","Sandstorm","Dense fog","Strong fog","Moderate haze","Heavy haze","Severe haze","Heavy fog","Extra heavy fog",
            "Hot","Cold","Unknown"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //通过泛型来指定要适配的数据类型,然后在构造函数中把适配的数据传入。
        //android.R.layout.simple_list_item_1是ListView内置的一个子项布局,里面只有一个TextView,可显示一段文本
        //data表示要适配的数据
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);
        ListView listView = (ListView)findViewById(R.id.list_view);
        //将构建好的适配器对象传进去
        listView.setAdapter(adapter);
    }
}

activity_main.xml中的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

App截图如下:
在这里插入图片描述
需要补充的是:
(1)simple_list_item_1(单行显示),此布局中只有一个TextView,是Android内置的布局文件,id为:android.R.id.text1;
(2)simple_list_item_2和two_line_list_item(双行显示),都有两个TextView:android.R.id.text1和android.R.id.text2,不同之处在于,前者两行字是不一样大的,而后者两行字一样大小。


二、自定义界面

由于只显示一段文本的ListView太过于单调,我们对其界面进行自定义。
首先,定义一个实体类,作为ListView的适配器类型,包含一组天气及其对应的名称。

public class Weather {
    private String name;
    private int imageId;

    public Weather(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }
}

其次,为ListView的子项指定一个我们自定义的布局,在layout目录下新建weather_item.xml。

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/weather_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/i_999_unknown"/>
    
    <TextView
        android:id="@+id/weather_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        android:text="未知天气"/>
    
</LinearLayout>

然后,创建一个自定义的适配器。这个适配器继承自ArrayAdapter,并将泛型指定为Weather类。

public class WeatherAdapter extends ArrayAdapter<Weather> {
    
    private int resourceId;
    
    //将上下文、ListView子项布局的id、数据 传递进来
    public WeatherAdapter(Context context, int textViewResourceId, List<Weather> obj){
        super(context, textViewResourceId, obj);
        resourceId = textViewResourceId;
    }
    
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Weather weather = getItem(position);//获取当前项的Weather实例
        //LayoutInflater的inflate()方法接收3个参数:需要实例化布局资源的id、ViewGroup类型视图组对象、false
        //false表示只让父布局中声明的layout属性生效,但不会为这个view添加父布局
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        //获取实例
        ImageView image = (ImageView) view.findViewById(R.id.weather_image);
        TextView name = (TextView) view.findViewById(R.id.weather_name);
        //设置图片和文字
        image.setImageResource(weather.getImageId());
        name.setText(weather.getName());
        return view;
    }
}

最后,修改MainActivity中的代码。

public class MainActivity extends AppCompatActivity {

    private List<Weather> weatherList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWeather();//初始化天气数据
        WeatherAdapter adapter = new WeatherAdapter(MainActivity.this, R.layout.weather_item, weatherList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }

    private void initWeather(){
    //苦力活动,请忽略
        Weather i_100 = new Weather("Sunny", R.drawable.i_100_sunny);
        weatherList.add(i_100);
        Weather i_101 = new Weather("Cloudy", R.drawable.i_101_cloudy);
        weatherList.add(i_101);
        Weather i_102 = new Weather("Few Clouds", R.drawable.i_102_few_clouds);
        weatherList.add(i_102);
        Weather i_103 = new Weather("Partly Cloudy", R.drawable.i_103_partly_cloudy);
        weatherList.add(i_103);
        Weather i_104 = new Weather("Overcast", R.drawable.i_104_overcast);
        weatherList.add(i_104);
        Weather i_200 = new Weather("Windy", R.drawable.i_200_windy);
        weatherList.add(i_200);
        Weather i_201 = new Weather("Calm", R.drawable.i_201_calm);
        weatherList.add(i_201);
        Weather i_202 = new Weather("Light Breeze", R.drawable.i_202_light_breeze);
        weatherList.add(i_202);
        Weather i_203 = new Weather("Moderate", R.drawable.i_203_moderate);
        weatherList.add(i_203);
        Weather i_204 = new Weather("Fresh Breeze", R.drawable.i_204_fresh_breeze);
        weatherList.add(i_204);
        Weather i_205 = new Weather("Strong Breeze", R.drawable.i_205_strong_breeze);
        weatherList.add(i_205);
        Weather i_206 = new Weather("High Wind", R.drawable.i_206_high_wind);
        weatherList.add(i_206);
        Weather i_207 = new Weather("Gale", R.drawable.i_207_gale);
        weatherList.add(i_207);
        Weather i_208 = new Weather("Strong Gale", R.drawable.i_208_strong_gale);
        weatherList.add(i_208);
        Weather i_209 = new Weather("Storm", R.drawable.i_209_storm);
        weatherList.add(i_209);
        Weather i_210 = new Weather("Violent Storm", R.drawable.i_210_violent_storm);
        weatherList.add(i_210);
        Weather i_211 = new Weather("Hurricane", R.drawable.i_211_hurricane);
        weatherList.add(i_211);
        Weather i_212 = new Weather("Tornado", R.drawable.i_212_tornado);
        weatherList.add(i_212);
        Weather i_213 = new Weather("Tropical Storm", R.drawable.i_213_tropical_storm);
        weatherList.add(i_213);
        Weather i_300 = new Weather("Shower Rain", R.drawable.i_300_shower_rain);
        weatherList.add(i_300);
        Weather i_301 = new Weather("Heavy Shower Rain", R.drawable.i_301_heavy_shower_rain);
        weatherList.add(i_301);
        Weather i_302 = new Weather("Thundershower", R.drawable.i_302_thundershower);
        weatherList.add(i_302);
        Weather i_303 = new Weather("Heavy Thunderstorm", R.drawable.i_303_heavy_thunderstorm);
        weatherList.add(i_303);
        Weather i_304 = new Weather("Thundershower with hail", R.drawable.i_304_thundershower_with_hail);
        weatherList.add(i_304);
        Weather i_305 = new Weather("Light Rain", R.drawable.i_305_light_rain);
        weatherList.add(i_305);
        Weather i_306 = new Weather("Moderate Rain", R.drawable.i_306_moderate_rain);
        weatherList.add(i_306);
        Weather i_307 = new Weather("Heavy Rain", R.drawable.i_307_heavy_rain);
        weatherList.add(i_307);
        Weather i_308 = new Weather("Extreme Rain", R.drawable.i_308_extreme_rain);
        weatherList.add(i_308);
        Weather i_309 = new Weather("Drizzle Rain", R.drawable.i_309_drizzle_rain);
        weatherList.add(i_309);
        Weather i_310 = new Weather("Storm", R.drawable.i_310_storm);
        weatherList.add(i_310);
        Weather i_311 = new Weather("Heavy Storm", R.drawable.i_311_heavy_storm);
        weatherList.add(i_311);
        Weather i_312 = new Weather("Severe Storm", R.drawable.i_312_severe_storm);
        weatherList.add(i_312);
        Weather i_313 = new Weather("Freezing Rain", R.drawable.i_313_freezing_rain);
        weatherList.add(i_313);
        Weather i_314 = new Weather("Light to moderate rain", R.drawable.i_314_light_to_moderate_rain);
        weatherList.add(i_314);
        Weather i_315 = new Weather("Moderate to heavy rain", R.drawable.i_315_moderate_to_heavy_rain);
        weatherList.add(i_315);
        Weather i_316 = new Weather("Heavy rain to storm", R.drawable.i_316_heavy_rain_to_storm);
        weatherList.add(i_316);
        Weather i_317 = new Weather("Storm to heavy storm", R.drawable.i_317_storm_to_heavy_storm);
        weatherList.add(i_317);
        Weather i_318 = new Weather("Heavy to severe storm", R.drawable.i_318_heavy_to_server_storm);
        weatherList.add(i_318);
        Weather i_399 = new Weather("Rain", R.drawable.i_399_rain);
        weatherList.add(i_399);
        Weather i_400 = new Weather("Light Snow", R.drawable.i_400_light_snow);
        weatherList.add(i_400);
        Weather i_401 = new Weather("Moderate Snow", R.drawable.i_401_moderate_snow);
        weatherList.add(i_401);
        Weather i_402 = new Weather("Heavy Snow", R.drawable.i_402_heavy_snow);
        weatherList.add(i_402);
        Weather i_403 = new Weather("Snowstorm", R.drawable.i_403_snowstorm);
        weatherList.add(i_403);
        Weather i_404 = new Weather("Sleet", R.drawable.i_404_sleet);
        weatherList.add(i_404);
        Weather i_405 = new Weather("Rain And Snow", R.drawable.i_405_rain_and_snow);
        weatherList.add(i_405);
        Weather i_406 = new Weather("Shower Snow", R.drawable.i_406_shower_snow);
        weatherList.add(i_406);
        Weather i_407 = new Weather("Snow Flurry", R.drawable.i_407_snow_flurry);
        weatherList.add(i_407);
        Weather i_408 = new Weather("Light to moderate snow", R.drawable.i_408_light_to_moderate_snow);
        weatherList.add(i_408);
        Weather i_409 = new Weather("Moderate to heavy snow", R.drawable.i_409_moderate_to_heavy_snow);
        weatherList.add(i_409);
        Weather i_410 = new Weather("Heavy snow to snowstorm", R.drawable.i_410_heavy_snow_to_snowstorm);
        weatherList.add(i_410);
        Weather i_499 = new Weather("Snow", R.drawable.i_499_snow);
        weatherList.add(i_499);
        Weather i_500 = new Weather("Mist", R.drawable.i_500_mist);
        weatherList.add(i_500);
        Weather i_501 = new Weather("Foggy", R.drawable.i_501_foggy);
        weatherList.add(i_501);
        Weather i_502 = new Weather("Haze", R.drawable.i_502_haze);
        weatherList.add(i_502);
        Weather i_503 = new Weather("Sand", R.drawable.i_503_sand);
        weatherList.add(i_503);
        Weather i_504 = new Weather("Dust", R.drawable.i_504_dust);
        weatherList.add(i_504);
        Weather i_507 = new Weather("Duststorm", R.drawable.i_507_duststorm);
        weatherList.add(i_507);
        Weather i_508 = new Weather("Sandstorm", R.drawable.i_508_sandstorm);
        weatherList.add(i_508);
        Weather i_509 = new Weather("Dense fog", R.drawable.i_509_dense_fog);
        weatherList.add(i_509);
        Weather i_510 = new Weather("Strong fog", R.drawable.i_510_strong_fog);
        weatherList.add(i_510);
        Weather i_511 = new Weather("Moderate haze", R.drawable.i_511_moderate_haze);
        weatherList.add(i_511);
        Weather i_512 = new Weather("Heavy haze", R.drawable.i_512_heavy_haze);
        weatherList.add(i_512);
        Weather i_513 = new Weather("Severe haze", R.drawable.i_513_severe_haze);
        weatherList.add(i_513);
        Weather i_514 = new Weather("Heavy fog", R.drawable.i_514_heavy_fog);
        weatherList.add(i_514);
        Weather i_515 = new Weather("Extra heavy fog", R.drawable.i_515_extra_heavy_fog);
        weatherList.add(i_515);
        Weather i_900 = new Weather("Hot", R.drawable.i_900_hot);
        weatherList.add(i_900);
        Weather i_901 = new Weather("Cold", R.drawable.i_901_cold);
        weatherList.add(i_901);
        Weather i_999 = new Weather("Unknown", R.drawable.i_999_unknown);
        weatherList.add(i_999);
    }
}

App截图如下:
在这里插入图片描述


三、提升运行效率

(1)在上一部分中我们在WeatherAdapter类中使用了getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。该方法存在一个弊端:每次都得将布局重新加载一次。当快速滚动ListView的时候,会影响App的性能。getView()方法中还有一个convertView参数,这个参数用于将之前加载好的布局进行缓存,便于重复利用。
我们在getView()中加入判断,如果convertView为null,则使用LayoutInflater去加载布局,不为空则直接重用convertView。
(2)修改之后,虽然不会重复加载布局,但是每次getView()方法还是会调用View的findViewById()方法来获取一次控件的实例。
我们使用ViewHolder来对这部分性能进行优化,ViewHolder通常出现在适配器里,为的是ListView滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。通过将所有控件的实例都缓存在了ViewHolder里,就不需要每次通过findViewById()方法来获取控件实例。
修改后的WeatherAdapter文件代码如下:

public class WeatherAdapter extends ArrayAdapter<Weather> {

    private int resourceId;

    //将上下文、ListView子项布局的id、数据 传递进来
    public WeatherAdapter(Context context, int textViewResourceId, List<Weather> obj){
        super(context, textViewResourceId, obj);
        resourceId = textViewResourceId;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Weather weather = getItem(position);//获取当前项的Weather实例
        //LayoutInflater的inflate()方法接收3个参数:需要实例化布局资源的id、ViewGroup类型视图组对象、false
        //false表示只让父布局中声明的layout属性生效,但不会为这个view添加父布局
        View view;
        ViewHolder viewHolder;
        //如果convertView为空,则使用LayoutInflater()去加载布局
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            viewHolder = new ViewHolder();
            //通过ViewHolder获取实例
            viewHolder.weatherImage = (ImageView) view.findViewById(R.id.weather_image);
            viewHolder.weatherName = (TextView) view.findViewById(R.id.weather_name);
            //将ViewHolder存储在view中
            view.setTag(viewHolder);
        }else{
            //否则,重用convertView
            view = convertView;
            //重新获取ViewHolder(利用View的getTag()方法,把ViewHolder重新取出)
            viewHolder = (ViewHolder)view.getTag();
        }
        //设置图片和文字
        viewHolder.weatherImage.setImageResource(weather.getImageId());
        viewHolder.weatherName.setText(weather.getName());
        return view;
    }

    //定义ViewHolder内部类,用于对控件实例进行缓存
    class ViewHolder{
        ImageView weatherImage;
        TextView weatherName;
    }
}

四、点击事件

修改MainActivity,使用OnItemClickListener()方法为ListView注册监听器。部分代码如下:

//为ListView添加点击事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Weather weather = weatherList.get(position);
                Toast.makeText(MainActivity.this, weather.getName(), Toast.LENGTH_SHORT).show();
            }
        });

接下来,我们对ListView中的setOnItemClickListener的OnItemClick()方法进行介绍:

//完整版
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {}
//简版
OnItemClick(AdapterView agr0, View arg1, int arg2, long arg3)

举个例子:X,Y两个ListView,X里面有1,2,3,4这4个item,Y里面有a,b,c,d这4个item,如果你点击了b这个item,如下:
(1)arg0,即parent
相当于ListView Y适配器的一个指针,可以通过它来获得Y里面装的一切东西,通俗点就是告诉你,你点的是Y不是X。
(2)arg1,即view
是你点b这个view句柄,就是你可以用这个view,来获得b里的控件的id后操作控件,通过它可以获得该项中的各个组件,例如:arg1.textview.setText(“abc”)。
(3)arg2,即position
是b在Y适配器里的位置(生成ListView时,适配器一个一个的做item,然后把他们按顺序排好队,再放到ListView里,意思就是这个b是第position号做好的)。
(4)arg3,即id
是b在ListView Y里面的第几行,在没有headerView、用户添加的view以及footerView的情况下,position和id的值是一样的。


猜你喜欢

转载自blog.csdn.net/MaybeForever/article/details/95998029