-
首先,我们创建一个UIActivity,实现它的布局。
<LinearLayout 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="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/img_empty_data"
android:contentDescription="@string/empty_view" />
</LinearLayout>
添加ListView控件很简单,只需要让它充满父布局,并为其指定一个id即可,至于下面的ImageView我们暂且不管它,后面再来说它的功能。
-
接下来我们来定义一个实体类,在此之前,我们先看看这个接口中提供的数据有哪些,打开http://www.wanandroid.com/article/list/0/json
这些就是这个接口返回的JSON数据,我们可以通过一个在线解析工具:JSON在线视图查看器来查看。
我们只需要将JSON数据全部复制到这个网站的JSON数据里面,然后点击左边的视图按钮,就可以看到这些JSON数据的结构了。
现在我们来决定我们需要展示什么,简单起见,我们就展示title、author、niceDate这些字段,不过还有一个link字段我们也需要获取到,也就是这篇文章的链接。所以我们需要定义这样一个Article实体类:
public class Article {
private String title;
private String author;
private String date;
private String link;
public Article(){}
public Article(String title, String author, String date, String link){
this.title = title;
this.author = author;
this.date = date;
this.link = link;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
-
下一步,我们要为ListView的item创建一个自定义布局,也就是我们要如何显示刚刚我们想要展示的内容。在layout目录下创建一个item_Article.xml布局文件。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:paddingTop="24dp"
android:paddingBottom="24dp">
<ImageView
android:id="@+id/img_article"
android:src="@drawable/article"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12sp"
android:layout_centerVertical="true"
android:contentDescription="@string/img_article"/>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@color/colorBlack"
android:layout_toRightOf="@id/img_article"
android:layout_toEndOf="@id/img_article"
android:layout_marginBottom="10dp"
android:singleLine="true"
android:ellipsize="end" />
<TextView
android:id="@+id/text_inform"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_article"
android:textSize="12sp"
android:layout_toRightOf="@id/img_article"
android:layout_toEndOf="@id/img_article"
android:layout_below="@id/text_title"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
android:singleLine="true"
android:ellipsize="end"/>
</RelativeLayout>
它的显示效果就是这样的:
-
然后,我们需要创建一个自定义适配器,用来将数据与我们的自定义item中的控件绑定起来,这样就能保证数据正确显示了。下面看代码:
public class ListViewAdapter extends BaseAdapter {
private List<Article> mArticles;
private LayoutInflater mInflater;
public ListViewAdapter(Context context, List<Article> articles){
this.mArticles = articles;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
//获取传入数组的长度,即item数量
return mArticles.size();
}
@Override
public Object getItem(int position) {
//获取指定item
return mArticles.get(position);
}
@Override
public long getItemId(int position) {
//获取某个item的位置
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null){
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.item_article, parent, false);
holder.mTitleText = (TextView) convertView.findViewById(R.id.text_title);
holder.mInformText = (TextView) convertView.findViewById(R.id.text_inform);
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
}
Article mArticle = mArticles.get(position);
holder.mTitleText.setText(mArticle.getTitle());
String format = parent.getResources().getString(R.string.text_article);
holder.mInformText.setText(String.format(format, mArticle.getAuthor(),mArticle.getDate()));
return convertView;
}
public final class ViewHolder{
TextView mTitleText;
TextView mInformText;
}
}
BaseAdapter是一个抽象类,让我们的ListViewAdapter继承它,重写它的getCount()、getItem()、getItenid()以及getView()方法。另外,我们在其内部定义了一个内部类ViewHolder。这种实现方式充分利用了ListView的视图缓存机制,避免每次在调用getView()方法时都要通过findViewById()实例化控件,当convertView为null时,使用LayoutInflater加载布局,并创建一个ViewHolder对象,并将控件的实例存放在ViewHolder中,这样就不需要每次都实例化控件,可以大大提高ListView的运行效率。
-
最后就是在UIActivity中获取数据并将数据传递给ListView显示出来。
public class UIActivity extends AppCompatActivity {
private List<Article> mArticles;
private ListView mListView;
private ListViewAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ui);
initView();
initData();
}
private void initView(){
mListView = (ListView)findViewById(R.id.list_view_ui);
mListView.setEmptyView(findViewById(R.id.list_view_ui));
}
private void initData(){
mArticles = new ArrayList<>();
String address = "http://www.wanandroid.com/article/list/0/json";
HttpUtil.sengHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String responseData) {
parseJSONWithJSONObject(responseData);
showArticles();
}
@Override
public void onError(Exception e) {
}
});
}
private void parseJSONWithJSONObject(String jsonData){
try {
JSONObject json = new JSONObject(jsonData);
JSONObject data = json.getJSONObject("data");
JSONArray datas = data.getJSONArray("datas");
for (int i=0;i<datas.length();i++){
JSONObject mJSONObject = datas.getJSONObject(i);
String title = mJSONObject.getString("title");
String author = mJSONObject.getString("author");
String date = mJSONObject.getString("niceDate");
String link = mJSONObject.getString("link");
Article mArticle = new Article(title, author, date, link);
mArticles.add(mArticle);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
private void showArticles(){
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter = new ListViewAdapter(UIActivity.this, mArticles);
mListView.setAdapter(mAdapter);
}
});
}
}
首先我们在initView()方法中对控件进行初始化,这里我们调用了ListView的setemptyView()方法,这个方法是为了设置一个空数据情况下的默认提示,即当我们的数据为空时,会显示这个ImageView,目的在于优化用户体验,不至于显示一片空白。然后在initData()方法中初始化Article数组,调用我封装好的HttpUtil类中的静态方法sendHttpRequest()发送HTTP请求,然后调用parseJSONWithJSONObject()方法解析JSON数据,将数据添加到Article数组中,最后调用showArticles()方法,实例化ListViewAdapter对象,通过ListView的setAdapter()方法将adapter传递进去,就实现了我们的ListView。当然,我们这里仅仅显示了一部分数据,接口中提供的数据是分页的,我们通过更改url中的页码(list和json中间的数字即为页码)可以获取到每一页的数据,这一部分功能我就不去实现了,感兴趣的话可以自己动动手。中间关于发送HTTP请求获取数据以及解析JSON数据的部分,我们会在后续章节了解到。下面看效果:
到这里,我们的ListView显示出了我们获取到的数据。但是还没有完,我们还需要给每一个item设置一个点击事件,用来显示我们获取到的link中的文章内容。
-
添加详情页
首先我们要创建一个WebActivity,因为我们的文章内容其实是一个网页,因此我们可以使用WebView来展示,所以在WebActivity的布局中我们需要添加一个WebView。
<FrameLayout 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="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
接下来我们要给ListView设置点击事件,每点击一个item,就将该item对应文章的link值传递给WebActivity,并且启动WebActivity。我在 Android基础回顾(二)| 关于Activity 这篇文章里面介绍过Activity之间传递数据的方法,这里就不赘述了。这里只需要修改initView()方法,直接看代码:
private void initView(){
mListView = (ListView)findViewById(R.id.list_view_ui);
mListView.setEmptyView(findViewById(R.id.list_view_ui));
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String link = mArticles.get(position).getLink();
String title = mArticles.get(position).getTitle();
Intent intent = new Intent(UIActivity.this, WebActivity.class);
intent.putExtra("link", link);
intent.putExtra("title", title);
startActivity(intent);
}
});
}
其实给ListView添加点击事件很简单,就是使用setOnItemClickListener()方法为ListView注册一个监听器,当用户点击ListView的任何一个子项时,就会回调onItemClick()方法,在这个方法中,可以通过position参数获取到对应子项的数据,然后通过Intent传递给WebActivity。接下来就是处理WebActivity中的逻辑了。
public class WebActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
initData();
}
private void initData(){
Intent intent = getIntent();
if (intent != null){
String address = intent.getStringExtra("link");
String title = intent.getStringExtra("title");
setTitle(title);
WebView mWebView = (WebView)findViewById(R.id.web_view);
mWebView.setWebViewClient(new WebViewClient());
mWebView.loadUrl(address);
}
}
}
我们获取到intent传递过来的数据,然后创建一个WebView对象,调用它的setWebViewClient()方法,并传入一个WebViewClient实例。它的作用是,当我们从一个网页跳到另一个网页时,我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。最后调用loadUrl()方法,加载页面。这样,我们的功能就完成了。当然,这只是单纯的实现功能,其实还有很多地方需要完善,比如给WebView添加加载动画等。感兴趣的话可以自己去探索实现。我们再来看一下效果吧!