✏️
Android应用开发
  • 简介
  • 目录
  • 第一章 熟悉Android Studio开发环境
    • 第一个项目——Hello World!
    • 第二个项目——计数器
    • 第三个项目——新闻阅读器
  • 第二章 活动、组件及布局
    • 第四个项目——消息发送
    • 第五个项目——计数器2
    • 第六个项目——登陆界面
    • 第七个项目——新闻列表
  • 第三章 数据持久化
    • 第八个项目——登陆界面2
    • 第九个项目——新闻列表2
  • 第四章 内容提供器与数据共享
    • 第十个项目——音乐播放器1
  • 第五章 多媒体与服务
    • 第十一个项目——音乐播放器2
    • 第十二个项目——音乐播放器3
  • 第六章 绑定服务与自定义广播
    • 第十三个项目——音乐播放器4
  • 第七章 网络与多线程
    • 第十四个项目——新闻列表3
  • 附录 扩展项目
    • 第十五个项目——使用ViewHolder提升ListView显示性能
    • 第十六个项目——使用SwipeRefreshLayout实现下拉刷新
    • 第十七个项目——使用接口回调实现删除ListView列表中的Item数据
    • 第十八个项目——使用RecyclerView显示列表数据
    • 第十九个项目——使用Fragment组织UI页面
    • 第二十个项目——使用CursorAdapter进行数据绑定及渲染
    • 第二十一个项目——使用Loader实现异步数据加载操作
    • 第二十二个项目——使用TimerTask及Handler实现秒表计时器
    • 第二十三个项目——使用AsyncTask进行网络异步请求
Powered by GitBook
On this page
  • 实验目的
  • 实验要求
  • 实验内容
  • 步骤一,创建Android工程
  • 步骤二,修改activity_main.xml布局
  • 步骤三,准备ListView的数据源
  • 步骤四,构造ListView所需的Adapter对象
  • 步骤五,设置SimpleAdapter,在ListView中显示新闻标题及作者
  • 步骤六,自定义Item布局
  • 步骤七,构造NewsAdapter适配器类
  • 实验小结

Was this helpful?

  1. 第二章 活动、组件及布局

第七个项目——新闻列表

Previous第六个项目——登陆界面Next第三章 数据持久化

Last updated 4 years ago

Was this helpful?

实验目的

  • 掌握ListView控件的基本用法;

  • 掌握ListView自定义Item布局的方法;

  • 掌握ArrayAdapter的基本用法及自定义Adapter的方法;

  • 掌握CardView控件的基本用法;

实验要求

  • 创建布局文件并对根据要求对布局进行设置;

实验内容

在本实验项目中,要求实现如所示的新闻阅读器应用。

图1. Code06最终运行效果

步骤一,创建Android工程

打开Android Studio创建名为Code06的工程,选择Empty Activity模板。

步骤二,修改activity_main.xml布局

在activity_main.xml中添加一个ListView作为根容器的唯一子控件,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/lv_news_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

步骤三,准备ListView的数据源

简便起见,我们将所有的新闻数据作为字符串资源放入arrays.xml文件中。 在工程窗口中右击app模块文件夹,选择【New】-> 【Android Resource File】菜单,弹出New Resource File窗口,设置文件名为arrays。 在array.xml文件中加入titles及authors两个字符串数组,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="titles">
        <item>Is Tomato a Vegetable or a Fruit?</item>
        <item>Why do Clocks Run Clockwise?</item>
        <item>Who Invented The Pen?</item>
        <item>Abdul Kareem’s Forest</item>
        <item>Venice is Sinking </item>
        <item>Martin Luther King, Jr. </item>
        <item>Charlie Chaplin </item>
        <item>How do Satellites Stay Up? </item>
        <item>Programmed to learn </item>
        <item>How does a Submarine Work? </item>
        <item>Where Did Numerals Originate? </item>
        <item>Why’s the Sun Red during Sunrise and Sunset? </item>
        <item>Why can’t the Sun melt Snow? </item>
        <item>How are Stars Named?</item>
        <item>How Far Away are the Stars? </item>
        <item>An Organism that is visible from Space</item>
        <item>Why Do We Have Wrinkly Fingers After Swimming? </item>
        <item>Why doesn’t our stomach get digested? </item>
        <item>What Is The Origin Of Silk Fabric? </item>
    </string-array>
    <string-array name="authors">
        <item>Chitra Padmanabhan</item>
        <item>Rama Kumaraswamy Thoopal</item>
        <item>Gargi Pant</item>
        <item>Brishti Bandyopadhyay</item>
        <item>Rama Kumaraswamy Thoopal</item>
        <item>Team Pitara</item>
        <item>Team Pitara</item>
        <item>Jim Nicholls</item>
        <item>Chitra Padmanabhan</item>
        <item>Ajay Dasgupta</item>
        <item>Bhaswati Ghosh</item>
        <item>Rama Kumaraswamy Thoopal</item>
        <item>Woodpecker</item>
        <item>Saakshi Khanna</item>
        <item>Ajay Dasgupta</item>
        <item>Gargi Pant</item>
        <item>Ajay Dasgupta</item>
        <item>Gargi Pant</item>
        <item>Gargi Pant</item>
    </string-array>
</resources>

步骤四,构造ListView所需的Adapter对象

ListView控件需要通过setAdapter()方法设置数据适配器,并绑定每个Item对应的布局。在MainActivity类中进行此操作。

1. 读取arrays.xml文件中的titles、authors字符串数组资源。

在MainActivity类中定义了titles、authors两个字符串数组,并使用getResources()得到Resources对象,并通过该对象的getStringArray的方法获取arrays.xml文件中定义的字符串数组资源。

public class MainActivity extends AppCompatActivity {

    private String[] titles = null;
    private String[] authors = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        titles = getResources().getStringArray(R.array.titles);
        authors = getResources().getStringArray(R.array.authors);

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(
            MainActivity.this, android.R.layout.simple_list_item_1, titles);

        ListView lvNewsList = findViewById(R.id.lv_news_list);
        lvNewsList.setAdapter(adapter);
    }
}

2. 设置ArrayAdapter,指定Item布局。

在本步骤中使用ArrayAdapter绑定titles字符串数组作为ListView控件的数据源。并指定Item的布局为系统预定义的 android.R.layout.simple_list_item_1,该布局指包含1个TextView控件。

3. 编译并部署APK。

步骤五,设置SimpleAdapter,在ListView中显示新闻标题及作者

ArrayAdapter作为ListView的适配器使用时,只能用于Item中包含1个TextView的情况。如果要在列表中显示新闻的标题及作者,则需要使用SimpleAdapter作为适配器。

首先了解SimpleAdapter适配器构造函数如何使用,代码如下所示,其中:

  • Context context,表示上下文对象,可直接传递MainActivity.this对象;

  • List<? extends Map > data,表示绑定的数据List列表,其类型需为Map;

  • int resource,ListView的Item布局资源,可以为系统预定义或用户自定义的布局资源;

  • String[] from,Map中的String类型的Key,与to结合一起将Map中的该key对应的value绑定到to数组中的资源上;

  • int[] to,resource布局资源中对应的控件id,与from结合完成Map中value数据与控件的绑定;

    public SimpleAdapter(Context context, 
                        List<? extends Map<String, ?>> data, 
                        int resource, String[] from, int[] to);

1. 构造数据源的List对象。

使用List > dataList 替换掉之前定义的titles、authors数组。将数据源的构造操作 放入initData()方法中。

public class MainActivity extends AppCompatActivity {

    private static final String NEWS_TITLE = "news_title";
    private static final String NEWS_AUTHOR = "news_author";

    private List<Map<String, String> > dataList = new ArrayList<>();

    ...

    private void initData() {
        int length;
        titles = getResources().getStringArray(R.array.titles);
        authors = getResources().getStringArray(R.array.authors);

        if (titles.length > authors.length) {
            length = authors.length;
        } else {
            length = titles.length;
        }

        for (int i = 0; i < length; i++) {
            Map map = new HashMap();
            map.put(NEWS_TITLE, titles[i]);
            map.put(NEWS_AUTHOR, authors[i]);

            dataList.add(map);
        }
    }
 }

2. 构造SimpleAdapter适配器。

SimpleAdapter对象所需的context上下文参数、数据源对象均已经构造好。Item布局则使用 系统预定义的android.R.layout.simple_list_item_2资源,该资源包含两个TextView控件标签,具体可在Android Studio中查看。

from数组,则是构造List >对象时所使用到的两个key:NEWS_TITLE及NEWS_AUTHOR。

to数组中则是android.R.layout.simple_list_item_2资源中的两个TextView控件标签的id。

public class MainActivity extends AppCompatActivity {
    private static final String NEWS_TITLE = "news_title";
    private static final String NEWS_AUTHOR = "news_author";    
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();

        SimpleAdapter simpleAdapter = new SimpleAdapter(MainActivity.this,
                dataList, android.R.layout.simple_list_item_2,
                new String[]{NEWS_TITLE, NEWS_AUTHOR},
                new int[]{android.R.id.text1, android.R.id.text2});

        ListView lvNewsList = findViewById(R.id.lv_news_list);
        lvNewsList.setAdapter(simpleAdapter);         
    }

    ...
}

3. 编译并部署APK。

步骤六,自定义Item布局

1. 自定义list_item.xml布局。 在工程窗口中右击app模块文件夹,选择【New】-> 【Android Resource File】菜单, 弹出New Resource File窗口,将File Name设为list_item, Resource Type设为Layout。

list_item.xml布局中的根元素使用CardView卡片容器控件。要使用卡片容器控件,需要在app模块的build.gradle文件中加入该控件的依赖库,代码如下所示。

apply plugin: 'com.android.application'

...

dependencies {
    ...
    implementation 'com.android.support:cardview-v7:28.0.0'
    ...
}

在CardView中使用RelatvieLayout进行布局管理,其中引入ImageView显示新闻图片。 设定其图片拉伸属性scaleType为centerCrop。

新闻标题及作者对应的TextView控件则使用分别使用layout_below属性位于新闻图片及新闻标题下方,并设置合理的padding属性,list_item.xml布局代码下所示。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="8dp"
    app:cardCornerRadius="8dp"
    >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/iv_image"
            android:layout_width="match_parent"
            android:layout_height="128dp"
            android:scaleType="centerCrop"
            />

        <TextView
            android:id="@+id/tv_title"
            style="@style/TextAppearance.AppCompat.Subhead"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/iv_image"
            android:paddingTop="8dp"
            android:paddingStart="8dp"
            android:paddingBottom="4dp"
            android:textColor="@color/colorPrimary"
            android:textSize="18sp"
            android:textStyle="bold" />
        <TextView
            android:id="@+id/tv_subtitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_title"
            android:paddingStart="8dp"
            android:paddingBottom="4dp"
            android:textColor="@android:color/secondary_text_light"
            style="@style/TextAppearance.AppCompat.Subhead"/>
    </RelativeLayout>
</android.support.v7.widget.CardView>

2. 加入图片资源

将准备好的图片资源放入工程的res/drawable文件夹中,并且在arrays.xml文件中加入这些图片资源的drawable路径字符串数组,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    ...
    <string-array name="images">
        <item>@drawable/img_01</item>
        <item>@drawable/img_02</item>
        <item>@drawable/img_03</item>
        <item>@drawable/img_04</item>
        <item>@drawable/img_05</item>
        <item>@drawable/img_06</item>
        <item>@drawable/img_07</item>
        <item>@drawable/img_09</item>
        <item>@drawable/img_08</item>
        <item>@drawable/img_010</item>
        <item>@drawable/img_011</item>
        <item>@drawable/img_012</item>
        <item>@drawable/img_013</item>
        <item>@drawable/img_014</item>
        <item>@drawable/img_015</item>
        <item>@drawable/img_016</item>
        <item>@drawable/img_017</item>
        <item>@drawable/img_018</item>
        <item>@drawable/img_019</item>
    </string-array>
</resources>

步骤七,构造NewsAdapter适配器类

1. 构造News类。

News类用于存储新闻的标题、作者、新闻正文、对应的标题图,代码如下所示。 需要为News类中的每个成员变量设置set/get方法,可通过【Refactor】->【Encapsulate Fields...】 窗口选择所需封装的成员变量自动生成set/get方法。

public class News {
    private String mTitle;
    private String mAuthor;
    private String mContent;
    private int mImageId;

    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        this.mTitle = title;
    }
    ...
}

2. 构造NewsAdapter适配器类。

右击工程窗口中的app/java文件夹,选择【New】-> 【Java Class】,将类名命名为NewsAdapter。 继承的父类为ArrayAdapter。

对于继承至ArrayAdapter类的NewsAdapter子类,需要至少实现两个方法:构造器方法以及getView方法。

NewsAdapter类的构造器需要三个参数,分别为:

  • Context context,context上下文用于NewsAdapter类中LayoutInflater加载布局时使用;

  • int resourceId,用于设置ListView每个Item项时的布局;

  • List data,用于传递News对象列表;

NewsAdapter构造函数中首先调用父类的构造方法,再将这三个参数保存以便在getView方法中使用。

getView方法是用于ListView显示某一位置Item时进行回调的方法。该方法返回值为Item所需要加载的View控件。 而该方法的的三个参数含义为:

  • int position,当前Item对应的位置;

  • View convertView,针对ListView中缓存的不可见的Item的View对象;

  • ViewGroup parent,需要加载的View的父容器对象;

通过position参数,可以使用getItem方法获取对应的News对象。

针对第二个参数convertView,需要结合ViewHolder使用以减少每一次getView方法调用 都需要重新构造Item项View布局对象,从而提升ListView效率,在此先不做介绍,详见第\ref{sec:listviewdemo}章内容。

在getView方法中调用LayoutInflater加载指定的resourceId布局,第二个参数则为加载的布局对应的父容器控件对象, 第三个参数表示是否将加载的布局加入父容器控件中,在此情况下要选择false。

使用LayoutInflater加载完成布局后,可分别绑定新闻标题、新闻作者、新闻标题图控件。并根据position取到的News对象设置这三个控件的属性,从而完成当前Item对应的布局的加载及数据绑定操作。

package com.glriverside.xgqin.listviewdemo;

import android.content.Context;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class NewsAdapter extends ArrayAdapter<News> {

    private List<News> mNewsData;
    private Context mContext;
    private int resourceId;

    public NewsAdapter(Context context, int resourceId, List<News> data) {
        super(context, resourceId, data);
        this.mContext = context;
        this.mNewsData = data;
        this.resourceId = resourceId;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        News news = getItem(position);
        View view ;

        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);

        TextView tvTitle  = view.findViewById(R.id.tv_title);
        TextView tvAuthor = view.findViewById(R.id.tv_subtitle);
        ImageView ivImage = view.findViewById(R.id.iv_image);

        tvTitle.setText(news.getTitle());
        tvAuthor.setText(news.getAuthor());
        ivImage.setImageResource(news.getImageId());
        return view;
    }
}

3. 修改数据源的构造方式。

在MainActivity中修改从arrays.xml中加载新闻标题、作者、新闻标题图的方式。

首先使用List newsList作为存储加载数据的对象。并在initData方法中使用Resources 的obtainTypedArray方法获取在arrays.xml文件定义的新闻标题图drawable资源。

在onCreate方法中新建NewsAdapter,并把自定义的list_item布局,以及在initData中构造的newsList 对象作为其构造函数参数。

最后将通过ListView的setAdapter设置构造好的NewsAdapter适配器,完成数据源与ListView的绑定。

public class MainActivity extends AppCompatActivity {

    public static final String NEWS_ID = "news_id";
    private List<News> newsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();

        NewsAdapter newsAdapter = new NewsAdapter(MainActivity.this,
                R.layout.list_item, newsList);

        ListView lvNewsList = findViewById(R.id.lv_news_list);

        lvNewsList.setAdapter(newsAdapter);
    }

    private void initData() {
        int length;

        titles = getResources().getStringArray(R.array.titles);
        authors = getResources().getStringArray(R.array.authors);
        TypedArray images = getResources().obtainTypedArray(R.array.images);

        if (titles.length > authors.length) {
            length = authors.length;
        } else {
            length = titles.length;
        }

        for (int i = 0; i < length; i++) {
            News news = new News();
            news.setTitle(titles[i]);
            news.setAuthor(authors[i]);
            news.setImageId(images.getResourceId(i, 0));

            newsList.add(news);
        }
    }
}

4. 编译并部署APK。

实验小结

通过本次实验,你应该掌握了如下知识内容:

  • 使用ListView进行数据显示;

  • 自定义ListView的Item布局;

  • 自定义ArrayAdapter构造适配器;

  • 使用CardView卡片布局展示数据;

编译本项目,并部署运行,其运行效果如所示。

图2. Code06运行效果——仅显示新闻标题

编译本项目,并部署运行,其运行效果如所示。

图3. Code06运行效果——仅显示新闻标题及作者

要在ListView控件中的每一列中显示如所示新闻图片、标题、作者样式,需要自定ListView的Item布局。

编译本项目,并部署运行,其运行效果如所示。

图2. Code06运行效果——仅显示新闻标题
图3. Code06运行效果——仅显示新闻标题及作者
图1. Code06最终运行效果
图1. Code06最终运行效果
图1. Code06最终运行效果