第七个项目——新闻列表
实验目的
掌握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。
编译本项目,并部署运行,其运行效果如图2. Code06运行效果——仅显示新闻标题所示。

步骤五,设置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。
编译本项目,并部署运行,其运行效果如图3. Code06运行效果——仅显示新闻标题及作者所示。

步骤六,自定义Item布局
要在ListView控件中的每一列中显示如图1. Code06最终运行效果所示新闻图片、标题、作者样式,需要自定ListView的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。
编译本项目,并部署运行,其运行效果如图1. Code06最终运行效果所示。
实验小结
通过本次实验,你应该掌握了如下知识内容:
使用ListView进行数据显示;
自定义ListView的Item布局;
自定义ArrayAdapter构造适配器;
使用CardView卡片布局展示数据;
Last updated
Was this helpful?