Android中通过ListView的实现简单新闻列表
今天班上的同学问到我ListView怎么用,叫我帮他写个简单的新闻列表的例子,并且通过点击列表进入新闻详情也面. 于是就把这个小demo记了下来
首先要用到的工具:
有 Volley库 进行网络请求和网络图片请求,Volley库相信很多人都都知道是谷歌官方承认的轻量级网络请求库. 不知道怎么用的可以看看这里 Android Volley完全解析(一),初识Volley的基本用法;
还有我们需要的数据,因为是新闻头条的数据,所以我就在聚合数据上面申请了一个免费的新闻头条接口(注:我不是想给聚合数据打广告)
这是头条的API接口:"http://v.juhe.cn/toutiao/index?type=top&key=a1a755458cc22f129942b349 04feb820";通过网络请求会给我返回这样的一个Json数据:
{
"reason":"成功的返回",
"result":{
"stat":"1",
"data":[
{
"title":"第一次去乡下女友家,晚上听到岳母屋内的声音,当晚我果断分手",
"date":"2016-08-13 18:14",
"author_name":"十里桃花清浅",
"thumbnail_pic_s":"http://02.imgmini.eastday.com/mobile/20160813/20160813181412_fd3946829ed8efe15b1aa6b069821e18_1_mwpm_03200403.jpeg",
"thumbnail_pic_s02":"http://02.imgmini.eastday.com/mobile/20160813/20160813181412_fd3946829ed8efe15b1aa6b069821e18_1_mwpl_05500201.jpeg",
"thumbnail_pic_s03":"http://02.imgmini.eastday.com/mobile/20160813/20160813181412_fd3946829ed8efe15b1aa6b069821e18_1_mwpl_05500201.jpeg",
"url":"http://mini.eastday.com/mobile/160813181412874.html?qid=juheshuju",
"uniquekey":"160813181412874",
"type":"头条",
"realtype":"娱乐"
},
{
"title":"海青发布会满屏成熟性感的女人味扑鼻,网友大呼:没穿内裤吧..",
"date":"2016-08-13 19:22",
"author_name":"好好先生",
"thumbnail_pic_s":"http://01.imgmini.eastday.com/mobile/20160813/20160813192250_dea3715afc0314cf051880b4d8ddf0be_1_mwpm_03200403.jpeg",
"thumbnail_pic_s02":"http://01.imgmini.eastday.com/mobile/20160813/20160813192250_dea3715afc0314cf051880b4d8ddf0be_1_mwpl_05500201.jpeg",
"thumbnail_pic_s03":"http://01.imgmini.eastday.com/mobile/20160813/20160813192250_dea3715afc0314cf051880b4d8ddf0be_1_mwpl_05500201.jpeg",
"url":"http://mini.eastday.com/mobile/160813192250414.html?qid=juheshuju",
"uniquekey":"160813192250414",
"type":"头条",
"realtype":"娱乐"
}
]
},
"error_code":0
}
然后解析该数据就行了!
下面开始demo:
首先创建一个javaBean的类,用来保存请求回来的新闻数据:
public class NewsData {
private String newsTitle;//新闻标题
private String newsDate; //新闻发布时间
private String newsImgUrl; // 新闻图片Url地址
private String newsUrl; //新闻详情Url地址
}
加上对应的get和set方法就行了..
activity_main.xml中就放了一个ListView
这里也不贴上上来了....
直接来看看ListView的item中的布局吧:
创建一个list_item.xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/iv_img"
android:layout_width="80dp"
android:layout_height="80dp"
/>
<LinearLayout
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textSize="18dp"/>
<TextView
android:id="@+id/tv_date"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dp"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
通过这个布局文件写一个我们自己的Adapter类,创建文件MyAdapter.java继承BaseAdapter;
public class MyAdapter extends BaseAdapter {
private List<NewsData> datas = new ArrayList<NewsData>();//新闻列表集合
private Context context;
private LayoutInflater layoutInflater;
public MyAdapter(Context context, List<NewsData> datas) {
this.datas = datas;
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return datas.size(); //返回列表的长度
}
@Override
public NewsData getItem(int position) {
return datas.get(position); //通过列表的位置 获得集合中的对象
}
@Override
public long getItemId(int position) { // 获得集合的Item的位置
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.list_item, null);//找到布局文件
convertView.setTag(new ViewHolder(convertView));
}
initViews(getItem(position), (ViewHolder) convertView.getTag());
return convertView;
}
private void initViews(NewsData data, ViewHolder holder) {//初始化数据
/**
* 第一次初始话的时候通过 要请求的Url地址 为每个图片设置一个Tag标记,
* 然后在设置图片的时候判断Tag标记如果是才把图片设置到ImageView上,
* 这做的原因是为了防止ListView 中的图片错位...
*/
holder.ivImg.setTag(data.getNewsImgUrl());//设置Tag
//设置新闻标题为集合中获得的标题
holder.tvTitle.setText(data.getNewsTitle());
设置新闻发布时间为集合中获得的发布时间
holder.tvDate.setText(data.getNewsDate());
//通过集合中的图片地址获得图片并且设置到view上
getImage(this.context, data.getNewsImgUrl(), holder.ivImg);
}
protected class ViewHolder {
private ImageView ivImg;
private TextView tvTitle;
private TextView tvDate;
public ViewHolder(View view) {
ivImg = (ImageView) view.findViewById(R.id.iv_img);
tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvDate = (TextView) view.findViewById(R.id.tv_date);
}
}
public void getImage(Context context, String imgUrl,
final ImageView imageView) {
/**
* 检测图片的Tag值 ,如果根请求的地址相同 才做图片的网络请求.
*/
if (imageView.getTag().toString().equals(imgUrl)) {
RequestQueue mQueue = Volley.newRequestQueue(context);
ImageRequest imageRequest = new ImageRequest(imgUrl,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
imageView.setImageBitmap(response);//将返回的Bitmap显示子啊ImageView上
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
mQueue.add(imageRequest);
}
}
}
这里主要是为ListView写一个填充数据的适配器,通过这个适配器来确定ListView上每个Item显示的数据.. 上面写了一个获取图片的类,里面用的是Volley库中图片一种获取方法. 其他方法请访问:Android Volley完全解析(二),使用Volley加载网络图片
下面是MainActivity中代码:
public class MainActivity extends ActionBarActivity {
/**
* 新闻列表请求接口
*/
public static final String URL = "http://v.juhe.cn/toutiao/index?type=top&key=a1a755458cc22f129942b34904feb820";
/**
* ListView对象
*/
private ListView listView;
/**
* 新闻集合对象
*/
private List<NewsData> datas;
/**
* 自定义的Adapter对象
*/
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) this.findViewById(R.id.listView1);
datas = new ArrayList<NewsData>();
getDatas(URL);
/**
* 实例化Adapter对象(注意:必须要写在在getDatas() 方法后面,不然datas中没有数据)
*/
adapter = new MyAdapter(this, datas);
}
/**
* 通过接口获取新闻列表的方法
* @param url
*/
public void getDatas(String url){
final RequestQueue mQueue = Volley.newRequestQueue(this);
JsonObjectRequest stringRequest = new JsonObjectRequest(url, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
try {
/**
* 对返回的json数据进行解析,然后装入datas集合中
*/
JSONObject jsonObject2 = jsonObject.getJSONObject("result");
JSONArray jsonArray = jsonObject2.getJSONArray("data");
for (int i = 0; i <jsonArray.length() ; i++) {
JSONObject item = jsonArray.getJSONObject(i);
NewsData data = new NewsData();
data.setNewsTitle(item.getString("title"));
data.setNewsDate(item.getString("date"));
data.setNewsImgUrl(item.getString("thumbnail_pic_s"));
data.setNewsUrl(item.getString("url"));
datas.add(data);
}
} catch (JSONException e) {
e.printStackTrace();
}
/**
* 请求成功后为ListView设置Adapter
*/
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
}
}
);
mQueue.add(stringRequest);
}
}
上面的getDatas()用到了Volley库中的JSON请求:详情请看:Android Volley完全解析(一),初识Volley的基本用法;
json数据的解析一定要严格返回的数据进行解析,到这里 列表中应该就能看到,效果了;
注意: 最后一定要在AndroidManifest.xml中添加网络权限:
到这里我们就实现了将新闻显示在ListView中....
下面我们来实现新闻的详情页 创建个activity_news_info.xml文件 ;
布局如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/iv_img"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="120dp"
/>
<TextView
android:id="@+id/tv_title"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18dp"
android:layout_gravity="center_vertical"
android:singleLine="true"/>
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dp"
android:singleLine="true"/>
<View
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<WebView
android:id="@+id/wv_content"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
上半部分用于显示新闻图片、标题、时间,下半部分用了一个WebView 来显示新闻的内容,因为接口中返回的是一个新闻内容的Url地址,所以只有用WebView通过访问来加载新闻内容了.
然后创建NewsInfoActivity.java来绑定这个布局界面, 因为NewsInfoActivity要加载从MainActivity中通过ListView的Item点击传递过来相应的新闻信息,所以我需要在MainActivity中为ListView设置一个Item点击事件setOnItemClickListener().
(注:不要忘了在AndroidManifest.xml中注册这个Activity)
在MainActivity中的onCreate()方法中添加的如下代码:
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
/**
* 创建一个意图
*/
Intent intent = new Intent(MainActivity.this,NewsInfoActivity.class);
/**
* 在datas中通过点击的位置position通过get()方法获得具体某个新闻
* 的数据然后通过Intent的putExtra()传递到NewsInfoActivity中
*/
intent.putExtra("newsTitle", datas.get(position).getNewsTitle());
intent.putExtra("newsDate", datas.get(position).getNewsDate());
intent.putExtra("newsImgUrl", datas.get(position).getNewsImgUrl());
intent.putExtra("newsUrl", datas.get(position).getNewsUrl());
MainActivity.this.startActivity(intent);//启动Activity
}
});
通过item的点击事件将数据传递到NewsInfoActivity中后在这个界面同样通过getIntent()的get()系列方法来获得传递过来的数据:
NewsInfoActivity中的代码如下:
public class NewsInfoActivity extends ActionBarActivity {
private ImageView ivImg;
private TextView tvTitle;
private TextView tvDate;
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news_info);
initViews();
}
/**
* 初始化数据
*/
public void initViews(){
ivImg = (ImageView) this.findViewById(R.id.iv_img);
tvTitle = (TextView) this.findViewById(R.id.tv_title);
tvDate = (TextView) this.findViewById(R.id.tv_date);
webView = (WebView) this.findViewById(R.id.wv_content);
/**
* 获得传递过来的数据
*/
Intent intent = this.getIntent();
String newsTitle = intent.getStringExtra("newsTitle");
String newsDate = intent.getStringExtra("newsDate");
String newsImgUrl = intent.getStringExtra("newsImgUrl");
String newsUrl = intent.getStringExtra("newsUrl");
getImage(this, newsImgUrl, ivImg);
/**
* 显示新闻信息
*/
tvTitle.setText(newsTitle);
tvDate.setText(newsDate);
webView.loadUrl(newsUrl);
}
/**
* 加载网络图片
*/
public void getImage(Context context, String imgUrl,
final ImageView imageView) {
RequestQueue mQueue = Volley.newRequestQueue(context);
ImageRequest imageRequest = new ImageRequest(imgUrl,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
imageView.setImageBitmap(response);
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
mQueue.add(imageRequest);
}
}
刚才不是写了一个getImage()的方法吗为什么还要在写一个,将这个方法写成一个公共的不就行了? 因为Adapter中的getImage方法需要判断ImageView的Tag值,这里不需要判断,所以这里就另外写了一个,代码一样的,就少了个Tag的判断..到这里就全部结束了.........
看看效果:
项目不足之处:
1. 没有给图片做缓存,所以当超出界面的item 就会被系统回收,当在回到刚才的位置时又会重新加载数据
2. 因为Volley都是异步加载在ListView滑动的时候也会加载数据,所以会造成滑动时有卡顿的现象
3.图片过多会出现内存溢出的现象