android应用开发-从设计到实现 4-9 天气指数列表的布局

版权声明:本文为博主原创文章,禁止转载,违者必究。 https://blog.csdn.net/anddlecn/article/details/78020330

天气指数列表的布局

天气相关信息是通过列表方式呈现的。Android SDK提供的ListView控件就能实现这样功能。

ListView需要和Adapter配合使用,ListView负责内容的显示,Adapter负责为ListView提供要展示的数据。

要实现我们希望的展示效果,需要开发者自定义一个Adapter。那我们先看看ListView是怎么使用的。

ListView的基本方法

先在我们的工程项目中使用ListView展示一些简单的内容,大家熟悉熟悉使用ListView的套路,

  1. activity_main.xml布局文件中,给ListView布局增加一个id叫做weather_index_info_list

    <ListView
        android:id="@+id/weather_index_info_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    </ListView>
  2. MainActivity.java的源码文件里,在界面创建的时候(onCreate()当中),通过代码获取ListView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("TEST", "Weather app launched");
    
        ListView lv = (ListView) findViewById(R.id.weather_index_info_list);
    
    }

    java源码通过R.id.weather_index_info_list,将布局文件中的ListView找了出来,转换成了可以通过java代码操作的对象。

  3. 创建一个Adapter负责为ListView提供数据。Android SDK提供了很多现成的Adapter-ArrayAdapter CursorAdapter SimpleAdapter等等,不过现在我们不需要去知道每个的用法,只要知道它们都是BaseAdapter的子类就行。它们的存在大大的简化了Adapter的使用。

    这里我先选择ArrayAdapter,来展示下它的用法。将要显示的数据和显示这项数据项的布局设置给Adapter

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        //每一项要显示的数据是一个字符串,这里设置显示3项
        String data[] = {"a", "b", "c"};
        //指定显示的数据内容,以及显示每项内容的布局文件
        ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);
    }

    Android SDK提供了一些常用的数据项布局方式android.R.layout.simple_list_item_1 android.R.layout.simple_list_item_2等等。我们也可以自己设计每一项的布局方式。

    list_view_adapter_relation-wc400

  4. Adapter设置给ListView,数据将以列表的形式被展示,

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        lv.setAdapter(adapter);
    }
  5. 为显示的每个item添加,点击时代响应处理函数;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        lv.setOnItemClickListener(new OnItemClickListener() {
    
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //添加需要响应的操作
            }
        });
    }

综合以上的代码,就是,

@Override
    protected void onCreate(Bundle savedInstanceState) {
    ......
    ListView lv = (ListView) findViewById(R.id.weather_index_info_list);
    String data[] = {"a", "b", "c"};
    ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);
    lv.setAdapter(adapter);
    lv.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //添加需要响应的操作
        }
    });
}

最后的界面效果就是这样,

运行以后,就能看到a b c以列表的形式,在界面上展现出来了。

weather_app_list_view_demo-wc250

如果需要展示的数据有变化,就需要更新ListView

ListView的更新需要在主线程进行(UI线程)。如果在其他线程更新,系统就会报错,程序崩溃,并提示你不能在非UI线程更新界面元素

所以修改了Adapter中要展示的数据后,需要使用AdapternotifyDataSetChanged()通知主线程自动刷新界面;

例如,

//要显示的数据用链表的形式保存;
List data = new ArrayList<String>();
data.add("a");
data.add("b");
data.add("c");
//指定显示的数据内容,以及显示每项内容的布局文件
ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);

......
//主线程中修改显示的数据项
data.add("d");
//并使用notifyDataSetChanged()刷新界面
ArrayAdapter.notifyDataSetChanged();

自定义Adapter

虽然Android SDK为我们提供了好几种现成的Adapter使用,但有时它们也并不能完全符合我们的要求,比如满足我们希望的天气信息项界面。

因此,我们准备自定义一个Adapter。

其实SimpleAdapter是可以满足要求的,但是为了下一阶段能容易的使用Material Design推荐的列表控件,我就先讲一讲如何自定义Adapter,降低后面的学习难度。

定义数据项的布局

为了让列表的数据项按照我们设计的模样显示,我们需要为它设计一个布局,把天气相关的指数信息展示上去。

weahter_more_info_item_layout-wc250

数据项的布局定义在res\layout\weather_index_info_item_layout.xml文件中(如何创建这种布局文件,前面已经介绍过了)。

数据项布局的方案如下,

weahter_more_info_item_layout_solution-wc400

元素 控件 属性 id名称
指数图标 ImageView 左边距 16dp 内容居中 weather_index_info_icon
指数名称 TextView 左边距 72dp 内容垂直居中 weather_index_info_name
指数取值 TextView 有边距 16dp 内容垂直居中 weather_index_info_value
数据项的整体布局 FrameLayout 高度48dp

* 数据项是一个FrameLayout,放到这个布局中的所有元素就像千层饼一样,都是一层一层堆叠起来的;

![framelayout_principle-wc300](media/framelayout_principle.png)
高度是设计规范中定义的`48dp`,

```xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp">

</FrameLayout>
```
  • 指数图标使用ImageView控件,给它的android:scaleType属性设置center,让缩略图垂直居中放置,图片预设成ic_air_quality;左边距是16dp

    <ImageView
        android:id="@+id/weather_index_info_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_air_quality"
        android:scaleType="center"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"/>
  • 指数名称使用TextView控件,它的左边距是72dp,垂直居中放置,字体的大小和颜色按照设计的规范来指定,

    <TextView
        android:id="@+id/weather_index_info_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="湿度"
        android:layout_marginLeft="72dp"
        android:layout_gravity="center_vertical"
        android:textSize="16sp"
        android:textColor= "#DE000000"/>
  • 指数取值使用TextView控件,它的右边距是16dp,垂直居中、整体靠右放置-center_vertical|right,字体的大小和颜色按照设计的规范来指定,

    <TextView
        android:id="@+id/weather_index_info_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="30%"
        android:layout_marginRight="16dp"
        android:layout_gravity="center_vertical|right"
        android:textSize="14sp"
        android:textColor= "#8A000000"/>

综合起来,如下面的布局源码,

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="48dp">

    <ImageView
        android:id="@+id/weather_index_info_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_air_quality"
        android:scaleType="center"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"/>

    <TextView
        android:id="@+id/weather_index_info_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="湿度"
        android:layout_marginLeft="72dp"
        android:layout_gravity="center_vertical"
        android:textSize="16sp"
        android:textColor= "#DE000000" />

    <TextView
        android:id="@+id/weather_index_info_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="30%"
        android:layout_marginRight="16dp"
        android:layout_gravity="center_vertical|right"
        android:textSize="14sp"
        android:textColor= "#8A000000"/>

</FrameLayout>

最后效果图:

weahter_more_info_item_layout_complete-wc300

实现同一个布局效果的方案可以有很多种。有的方案在代码上很容易实现,有的方案可能还会考虑到布局刷新的效率。随着开发经验的增多,对各种布局实现原理的深入理解,大家会进一步的加深对布局的认识。

定义数据项的数据结构

为了提供显示的数据内容,要先定义一个存放数据的数据结构-WeatherIndexInfo

首先要重新创建一个java源文件,定义数据结构的类,

create_java_class_file-wc500

给类命名WeatherIndexInfo

create_java_class_file2-wc400

java目录对应的包目录下,就生成了WeatherIndexInfo.java文件。

我们要在这个类里面存放3个数据:图标、名称、数值。

组件名称 存储类型 说明 字段名称
指数图标 int 存储图标对应的资源id typeResId
指数名称 String 数据项的名称,描述含义 name
指数取值 String 显示名称对应的数据取值 value
public class WeatherIndexInfo {

    public int typeResId = 0;
    public String name = "";
    public String value = "";

}

数据结构的定义我们暂时就到这里,这个结构在后面还会做出调整,需要和网络端的数据进行配合。我们先到此处,只要能显示列表就可以了。

定义Adapter

现在开始自定义Adapter-WeatherIndexInfoAdapter

Adapter都是继承自BaseAdapter的,我们这里的Adapter准备继承自它的一个子类ArrayAdapter。因为ArrayAdapter在最原始的基础上作出了改进,我们再在它的基础上做一些小的调整就可以用了,而不用完全从头来过。

创建WeatherIndexInfoAdapter.java的新类。

  1. 继承ArrayAdapter,将显示的数据类型指定成WeatherIndexInfo;重新构造函数,传入Context,数据项布局使用的布局ID,要显示的数据列表;重写它的getView()方法;

    public class WeatherIndexInfoAdapter extends ArrayAdapter<WeatherIndexInfo> {
    
        public WeatherIndexInfoAdapter(Context context, int resource, List<WeatherIndexInfo> objects) {
            super(context, resource, objects);
    
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            return null;
        }
    }
  2. 在构造函数中,保存好布局ID以后使用,通过Context获取Inflater,为以后数据项布局的创建做准备,

    private final LayoutInflater mInflater;
    private final int mResource;
    
    public WeatherIndexInfoAdapter(Context context, int resource, List<WeatherIndexInfo> objects) {
        super(context, resource, objects);
        mInflater = LayoutInflater.from(context);
        mResource = resource;
    }
  3. getView()函数中,创建数据项的布局,并为他们赋值,最后将这个布局返回给ListView,让它显示,

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        if (convertView == null) {
            convertView = mInflater.inflate(mResource, parent, false);
        }
    
        WeatherIndexInfo item = getItem(position);
    
        ImageView icon = (ImageView) convertView.findViewById(R.id.weather_index_info_icon);
        icon.setImageResource(item.typeResId);
    
        TextView name = (TextView) convertView.findViewById(R.id.weather_index_info_name);
        name.setText(item.name);
    
        TextView value = (TextView) convertView.findViewById(R.id.weather_index_info_value);
        value.setText(item.value);
    
        return convertView;
    }

    这里的convertView就是数据项所代表的那个布局,当ListView刚创建,还没有产生任何数据项的时候,它就是为null的,此时我们就需要创建一个布局,并通过getView()将这个布局返回给ListView

    假如ListView上的数据项布局已经足够了,那么这里传入的convertView就不会再是null,而是之前的某个数据项布局,我们就不必为此重新创建了,只需要更新上面的内容就好。这样提高了界面刷新的效率。

    当然,这里还能通过其他方法减少使用findViewById(),进一步提高效率,不过目前就不改进了,先把功能实现完成。

Adapter终于完成了。

使用Adapter

现在看看这个Adapter怎么用。

  1. MainActivity创建之时,我们在onCreate()中创建并设置WeatherIndexInfoAdapter

    public class MainActivity extends AppCompatActivity {
    
        //保存ListView控件
        private ListView mWeatherIndexInfoListView;
        //保存天气指数信息到列表当中
        private List<WeatherIndexInfo> mWeatherIndexInfoList;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ......
    
            mWeatherIndexInfoListView = (ListView) findViewById(R.id.weather_index_info_list);
            //创建保存数据的列表,数据暂时没有
            mWeatherIndexInfoList = new ArrayList<>();
            //创建一个还没有添加入数据的Adapter
            WeatherIndexInfoAdapter adapter = new WeatherIndexInfoAdapter(MainActivity.this, R.layout.weather_index_info_item_layout, mWeatherIndexInfoList);
            mWeatherIndexInfoListView.setAdapter(adapter);       
    
        }
    }

    在创建Adapter的时候,把weather_index_info_item_layout.xml代表的布局传给了AdapterAdapter将使用它来生成每个数据项的界面。

  2. 添加一些虚假的数据到Adapter中,并使用notifyDataSetChanged()刷新看看效果,

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ......
    
        WeatherIndexInfo data1 = new WeatherIndexInfo();
        data1.typeResId = R.mipmap.ic_wind_level;
        data1.name = "风力";
        data1.value = "3级";
        mWeatherIndexInfoList.add(data1);
    
        WeatherIndexInfo data2 = new WeatherIndexInfo();
        data2.typeResId = R.mipmap.ic_wind_direction;
        data2.name = "风向";
        data2.value = "东南";
        mWeatherIndexInfoList.add(data2);
    
        WeatherIndexInfo data3 = new WeatherIndexInfo();
        data3.typeResId = R.mipmap.ic_humidity_level;
        data3.name = "湿度";
        data3.value = "60%";
        mWeatherIndexInfoList.add(data3);
    
        WeatherIndexInfo data4 = new WeatherIndexInfo();
        data4.typeResId = R.mipmap.ic_air_quality;
        data4.name = "空气质量";
        data4.value = "重污染";
        mWeatherIndexInfoList.add(data4);
    
        WeatherIndexInfo data5 = new WeatherIndexInfo();
        data5.typeResId = R.mipmap.ic_sport_level;
        data5.name = "运动";
        data5.value = "不合适";
        mWeatherIndexInfoList.add(data5);
    
        WeatherIndexInfo data6 = new WeatherIndexInfo();
        data6.typeResId = R.mipmap.ic_ultraviolet_level;
        data6.name = "紫外线";
        data6.value = "强";
        mWeatherIndexInfoList.add(data6);
    
        adapter.notifyDataSetChanged();
    
    }

    至此,视频列表的界面就能看到视频列表了。

    weather_more_info_list_complete-wc250

天气指数列表的布局

天气相关信息是通过列表方式呈现的。Android SDK提供的ListView控件就能实现这样功能。

ListView需要和Adapter配合使用,ListView负责内容的显示,Adapter负责为ListView提供要展示的数据。

要实现我们希望的展示效果,需要开发者自定义一个Adapter。那我们先看看ListView是怎么使用的。

ListView的基本方法

先在我们的工程项目中使用ListView展示一些简单的内容,大家熟悉熟悉使用ListView的套路,

  1. activity_main.xml布局文件中,给ListView布局增加一个id叫做weather_index_info_list

    <ListView
        android:id="@+id/weather_index_info_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    </ListView>
  2. MainActivity.java的源码文件里,在界面创建的时候(onCreate()当中),通过代码获取ListView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("TEST", "Weather app launched");
    
        ListView lv = (ListView) findViewById(R.id.weather_index_info_list);
    
    }

    java源码通过R.id.weather_index_info_list,将布局文件中的ListView找了出来,转换成了可以通过java代码操作的对象。

  3. 创建一个Adapter负责为ListView提供数据。Android SDK提供了很多现成的Adapter-ArrayAdapter CursorAdapter SimpleAdapter等等,不过现在我们不需要去知道每个的用法,只要知道它们都是BaseAdapter的子类就行。它们的存在大大的简化了Adapter的使用。

    这里我先选择ArrayAdapter,来展示下它的用法。将要显示的数据和显示这项数据项的布局设置给Adapter

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        //每一项要显示的数据是一个字符串,这里设置显示3项
        String data[] = {"a", "b", "c"};
        //指定显示的数据内容,以及显示每项内容的布局文件
        ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);
    }

    Android SDK提供了一些常用的数据项布局方式android.R.layout.simple_list_item_1 android.R.layout.simple_list_item_2等等。我们也可以自己设计每一项的布局方式。

    list_view_adapter_relation-wc400

  4. Adapter设置给ListView,数据将以列表的形式被展示,

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        lv.setAdapter(adapter);
    }
  5. 为显示的每个item添加,点击时代响应处理函数;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        lv.setOnItemClickListener(new OnItemClickListener() {
    
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //添加需要响应的操作
            }
        });
    }

综合以上的代码,就是,

@Override
    protected void onCreate(Bundle savedInstanceState) {
    ......
    ListView lv = (ListView) findViewById(R.id.weather_index_info_list);
    String data[] = {"a", "b", "c"};
    ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);
    lv.setAdapter(adapter);
    lv.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //添加需要响应的操作
        }
    });
}

最后的界面效果就是这样,

运行以后,就能看到a b c以列表的形式,在界面上展现出来了。

weather_app_list_view_demo-wc250

如果需要展示的数据有变化,就需要更新ListView

ListView的更新需要在主线程进行(UI线程)。如果在其他线程更新,系统就会报错,程序崩溃,并提示你不能在非UI线程更新界面元素

所以修改了Adapter中要展示的数据后,需要使用AdapternotifyDataSetChanged()通知主线程自动刷新界面;

例如,

//要显示的数据用链表的形式保存;
List data = new ArrayList<String>();
data.add("a");
data.add("b");
data.add("c");
//指定显示的数据内容,以及显示每项内容的布局文件
ArrayAdapter adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1 , data);

......
//主线程中修改显示的数据项
data.add("d");
//并使用notifyDataSetChanged()刷新界面
ArrayAdapter.notifyDataSetChanged();

自定义Adapter

虽然Android SDK为我们提供了好几种现成的Adapter使用,但有时它们也并不能完全符合我们的要求,比如满足我们希望的天气信息项界面。

因此,我们准备自定义一个Adapter。

其实SimpleAdapter是可以满足要求的,但是为了下一阶段能容易的使用Material Design推荐的列表控件,我就先讲一讲如何自定义Adapter,降低后面的学习难度。

定义数据项的布局

为了让列表的数据项按照我们设计的模样显示,我们需要为它设计一个布局,把天气相关的指数信息展示上去。

weahter_more_info_item_layout-wc250

数据项的布局定义在res\layout\weather_index_info_item_layout.xml文件中(如何创建这种布局文件,前面已经介绍过了)。

数据项布局的方案如下,

weahter_more_info_item_layout_solution-wc400

元素 控件 属性 id名称
指数图标 ImageView 左边距 16dp 内容居中 weather_index_info_icon
指数名称 TextView 左边距 72dp 内容垂直居中 weather_index_info_name
指数取值 TextView 有边距 16dp 内容垂直居中 weather_index_info_value
数据项的整体布局 FrameLayout 高度48dp

* 数据项是一个FrameLayout,放到这个布局中的所有元素就像千层饼一样,都是一层一层堆叠起来的;

![framelayout_principle-wc300](media/framelayout_principle.png)
高度是设计规范中定义的`48dp`,

```xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp">

</FrameLayout>
```
  • 指数图标使用ImageView控件,给它的android:scaleType属性设置center,让缩略图垂直居中放置,图片预设成ic_air_quality;左边距是16dp

    <ImageView
        android:id="@+id/weather_index_info_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_air_quality"
        android:scaleType="center"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"/>
  • 指数名称使用TextView控件,它的左边距是72dp,垂直居中放置,字体的大小和颜色按照设计的规范来指定,

    <TextView
        android:id="@+id/weather_index_info_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="湿度"
        android:layout_marginLeft="72dp"
        android:layout_gravity="center_vertical"
        android:textSize="16sp"
        android:textColor= "#DE000000"/>
  • 指数取值使用TextView控件,它的右边距是16dp,垂直居中、整体靠右放置-center_vertical|right,字体的大小和颜色按照设计的规范来指定,

    <TextView
        android:id="@+id/weather_index_info_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="30%"
        android:layout_marginRight="16dp"
        android:layout_gravity="center_vertical|right"
        android:textSize="14sp"
        android:textColor= "#8A000000"/>

综合起来,如下面的布局源码,

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="48dp">

    <ImageView
        android:id="@+id/weather_index_info_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_air_quality"
        android:scaleType="center"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"/>

    <TextView
        android:id="@+id/weather_index_info_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="湿度"
        android:layout_marginLeft="72dp"
        android:layout_gravity="center_vertical"
        android:textSize="16sp"
        android:textColor= "#DE000000" />

    <TextView
        android:id="@+id/weather_index_info_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="30%"
        android:layout_marginRight="16dp"
        android:layout_gravity="center_vertical|right"
        android:textSize="14sp"
        android:textColor= "#8A000000"/>

</FrameLayout>

最后效果图:

weahter_more_info_item_layout_complete-wc300

实现同一个布局效果的方案可以有很多种。有的方案在代码上很容易实现,有的方案可能还会考虑到布局刷新的效率。随着开发经验的增多,对各种布局实现原理的深入理解,大家会进一步的加深对布局的认识。

定义数据项的数据结构

为了提供显示的数据内容,要先定义一个存放数据的数据结构-WeatherIndexInfo

首先要重新创建一个java源文件,定义数据结构的类,

create_java_class_file-wc500

给类命名WeatherIndexInfo

create_java_class_file2-wc400

java目录对应的包目录下,就生成了WeatherIndexInfo.java文件。

我们要在这个类里面存放3个数据:图标、名称、数值。

组件名称 存储类型 说明 字段名称
指数图标 int 存储图标对应的资源id typeResId
指数名称 String 数据项的名称,描述含义 name
指数取值 String 显示名称对应的数据取值 value
public class WeatherIndexInfo {

    public int typeResId = 0;
    public String name = "";
    public String value = "";

}

数据结构的定义我们暂时就到这里,这个结构在后面还会做出调整,需要和网络端的数据进行配合。我们先到此处,只要能显示列表就可以了。

定义Adapter

现在开始自定义Adapter-WeatherIndexInfoAdapter

Adapter都是继承自BaseAdapter的,我们这里的Adapter准备继承自它的一个子类ArrayAdapter。因为ArrayAdapter在最原始的基础上作出了改进,我们再在它的基础上做一些小的调整就可以用了,而不用完全从头来过。

创建WeatherIndexInfoAdapter.java的新类。

  1. 继承ArrayAdapter,将显示的数据类型指定成WeatherIndexInfo;重新构造函数,传入Context,数据项布局使用的布局ID,要显示的数据列表;重写它的getView()方法;

    public class WeatherIndexInfoAdapter extends ArrayAdapter<WeatherIndexInfo> {
    
        public WeatherIndexInfoAdapter(Context context, int resource, List<WeatherIndexInfo> objects) {
            super(context, resource, objects);
    
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            return null;
        }
    }
  2. 在构造函数中,保存好布局ID以后使用,通过Context获取Inflater,为以后数据项布局的创建做准备,

    private final LayoutInflater mInflater;
    private final int mResource;
    
    public WeatherIndexInfoAdapter(Context context, int resource, List<WeatherIndexInfo> objects) {
        super(context, resource, objects);
        mInflater = LayoutInflater.from(context);
        mResource = resource;
    }
  3. getView()函数中,创建数据项的布局,并为他们赋值,最后将这个布局返回给ListView,让它显示,

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        if (convertView == null) {
            convertView = mInflater.inflate(mResource, parent, false);
        }
    
        WeatherIndexInfo item = getItem(position);
    
        ImageView icon = (ImageView) convertView.findViewById(R.id.weather_index_info_icon);
        icon.setImageResource(item.typeResId);
    
        TextView name = (TextView) convertView.findViewById(R.id.weather_index_info_name);
        name.setText(item.name);
    
        TextView value = (TextView) convertView.findViewById(R.id.weather_index_info_value);
        value.setText(item.value);
    
        return convertView;
    }

    这里的convertView就是数据项所代表的那个布局,当ListView刚创建,还没有产生任何数据项的时候,它就是为null的,此时我们就需要创建一个布局,并通过getView()将这个布局返回给ListView

    假如ListView上的数据项布局已经足够了,那么这里传入的convertView就不会再是null,而是之前的某个数据项布局,我们就不必为此重新创建了,只需要更新上面的内容就好。这样提高了界面刷新的效率。

    当然,这里还能通过其他方法减少使用findViewById(),进一步提高效率,不过目前就不改进了,先把功能实现完成。

Adapter终于完成了。

使用Adapter

现在看看这个Adapter怎么用。

  1. MainActivity创建之时,我们在onCreate()中创建并设置WeatherIndexInfoAdapter

    public class MainActivity extends AppCompatActivity {
    
        //保存ListView控件
        private ListView mWeatherIndexInfoListView;
        //保存天气指数信息到列表当中
        private List<WeatherIndexInfo> mWeatherIndexInfoList;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ......
    
            mWeatherIndexInfoListView = (ListView) findViewById(R.id.weather_index_info_list);
            //创建保存数据的列表,数据暂时没有
            mWeatherIndexInfoList = new ArrayList<>();
            //创建一个还没有添加入数据的Adapter
            WeatherIndexInfoAdapter adapter = new WeatherIndexInfoAdapter(MainActivity.this, R.layout.weather_index_info_item_layout, mWeatherIndexInfoList);
            mWeatherIndexInfoListView.setAdapter(adapter);       
    
        }
    }

    在创建Adapter的时候,把weather_index_info_item_layout.xml代表的布局传给了AdapterAdapter将使用它来生成每个数据项的界面。

  2. 添加一些虚假的数据到Adapter中,并使用notifyDataSetChanged()刷新看看效果,

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ......
    
        WeatherIndexInfo data1 = new WeatherIndexInfo();
        data1.typeResId = R.mipmap.ic_wind_level;
        data1.name = "风力";
        data1.value = "3级";
        mWeatherIndexInfoList.add(data1);
    
        WeatherIndexInfo data2 = new WeatherIndexInfo();
        data2.typeResId = R.mipmap.ic_wind_direction;
        data2.name = "风向";
        data2.value = "东南";
        mWeatherIndexInfoList.add(data2);
    
        WeatherIndexInfo data3 = new WeatherIndexInfo();
        data3.typeResId = R.mipmap.ic_humidity_level;
        data3.name = "湿度";
        data3.value = "60%";
        mWeatherIndexInfoList.add(data3);
    
        WeatherIndexInfo data4 = new WeatherIndexInfo();
        data4.typeResId = R.mipmap.ic_air_quality;
        data4.name = "空气质量";
        data4.value = "重污染";
        mWeatherIndexInfoList.add(data4);
    
        WeatherIndexInfo data5 = new WeatherIndexInfo();
        data5.typeResId = R.mipmap.ic_sport_level;
        data5.name = "运动";
        data5.value = "不合适";
        mWeatherIndexInfoList.add(data5);
    
        WeatherIndexInfo data6 = new WeatherIndexInfo();
        data6.typeResId = R.mipmap.ic_ultraviolet_level;
        data6.name = "紫外线";
        data6.value = "强";
        mWeatherIndexInfoList.add(data6);
    
        adapter.notifyDataSetChanged();
    
    }

    至此,视频列表的界面就能看到视频列表了。

    weather_more_info_list_complete-wc250

猜你喜欢

转载自blog.csdn.net/anddlecn/article/details/78020330
今日推荐