Bootstrap

Android中通过ListView的实现简单新闻列表

Android中通过ListView的实现简单新闻列表

今天班上的同学问到我ListView怎么用,叫我帮他写个简单的新闻列表的例子,并且通过点击列表进入新闻详情也面.     于是就把这个小demo记了下来


首先要用到的工具:

有 Volley库 进行网络请求和网络图片请求,Volley库相信很多人都都知道是谷歌官方承认的轻量级网络请求库. 不知道怎么用的可以看看这里 Android Volley完全解析(一),初识Volley的基本用法;

还有我们需要的数据,因为是新闻头条的数据,所以我就在聚合数据上面申请了一个免费的新闻头条接口(注:我不是想给聚合数据打广告) 
    这是头条的API接口:"http://v.juhe.cn/toutiao/index?type=top&key=a1a755458cc22f129942b34904feb820";通过网络请求会给我返回这样的一个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中添加网络权限:

[html]  view plain  copy
  1. <uses-permission android:name="android.permission.INTERNET"/>  



到这里我们就实现了将新闻显示在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.图片过多会出现内存溢出的现象

;