前言
前几天在聚合数据上看到了个新闻头条的接口,太兴奋了。平时写个新闻APP Demo各种爬数据,tomcat本地模拟,苦逼啊,有没有!既然别人提供了新闻接口,作为程序猿的我们怎么能不亲自动手玩下呢。
国际惯例,先来个效果图
聚合数据提供的新闻接口数据是json格式的,而且是免费无限次的!另外提醒一下,聚合数据上的接口是需要申请认证的。
根据接口我们拿到json格式的数据,项目中我们只需要title,date,author_name,thumbnail_pic_s,url四种数据,所以没必要全部解析。
下面就开始代码了
1、activity_main.xml,首先写个ListView控件,用来显示新闻。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" android:padding="5dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.app.newsdemo.MainActivity" tools:showIn="@layout/activity_main" android:background="#F0F3F6"> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
2、news_item.xml,自定义ListView布局,类似网易新闻的新闻界面。
<?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:orientation="vertical"> <!--<com.app.newsdemo.loopj.android.image.SmartImageView android:id="@+id/imgsrc" android:layout_width="100dp" android:layout_height="75dp" android:layout_centerVertical="true" android:padding="3dp" android:src="@mipmap/ic_launcher" />--> <ImageView android:id="@+id/imgsrc" android:layout_width="100dp" android:layout_height="75dp" android:layout_centerVertical="true" android:padding="3dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@id/imgsrc" android:paddingTop="4dp" android:text="这是标题" android:textColor="@android:color/darker_gray" android:textSize="20sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/title" android:layout_toRightOf="@id/imgsrc" android:orientation="horizontal" android:layout_alignParentBottom="true" android:paddingBottom="2dp" android:paddingTop="4dp"> <TextView android:id="@+id/author_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="2dp" android:layout_weight="1" android:text="来源" android:textColor="@android:color/darker_gray" android:textSize="15sp" /> <TextView android:id="@+id/date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="8dp" android:text="2016.3.25 14:35" android:textColor="@android:color/darker_gray" android:textSize="15sp" /> </LinearLayout> </RelativeLayout>
4、布局写完了,下面就是怎么去获取新闻数据。网络请求属于耗时操作,不能再UI线程中去执行网络请求任务,所以我们不能直接在主线程中操作,AsyncTask很好的帮我们解决了这个问题。AsyncTask是什么,这里我就不具体讲了,这不是这篇博客的重点,如果你不熟悉,建议你先去了解AsyncTask。
这里我定义一个NewsTask继承AsyncTask去下载网络数据。接口返回的数据是json格式的,所以必须要解析,当然你可以用GSON,FastJson第三方的东西解析。这里我就自己动手解析了。
这里的重点是,我们下载解析的数据怎么传给我们的主线程呢,接口回调,定义一个NewsCallBack接口,里面定义一个getNews方法,用来传数据。当主线程调用这个异步加载任务时,就可以通过实例化接口方法来获取数据了。
public class NewsTask extends AsyncTask<String, Void, ArrayList<News>> { public interface NewsCallBack{ void getNews(ArrayList<News> list); } private NewsCallBack news; private ProgressDialog pd; public NewsTask(NewsCallBack news, Context context) { this.news = news; pd = new ProgressDialog(context); pd.setMessage("loading..."); pd.setCancelable(false); } @Override protected void onPreExecute() { super.onPreExecute(); pd.show(); } @Override protected ArrayList<News> doInBackground(String... params) { try { HttpURLConnection conn = (HttpURLConnection) new URL(params[0]).openConnection(); conn.setRequestMethod("GET"); conn.connect(); if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int num = -1; StringBuffer sb = new StringBuffer(); while ((num = is.read(b)) != -1) { sb.append(new String(b,0,num)); } SystemClock.sleep(1000); String json = sb.toString(); ArrayList<News> list = new ArrayList<>(); News news; JSONObject jo = new JSONObject(json); if (jo.getString("reason").equals("成功的返回")) { JSONObject jo1 = jo.getJSONObject("result"); JSONArray ja = jo1.getJSONArray("data"); for (int i = 0; i < ja.length(); i++) { news = new News(); JSONObject obj = ja.getJSONObject(i); news.setTitle(obj.getString("title")); news.setDate(obj.getString("date")); news.setAuthor_name(obj.getString("author_name")); news.setThumbnail_pic_s(obj.getString("thumbnail_pic_s")); news.setUrl(obj.getString("url")); list.add(news); } return list; } } } catch (Exception e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(ArrayList<News> newses) { super.onPostExecute(newses); pd.dismiss(); if (news != null) { news.getNews(newses); } } }
7、前面我们通过HttpURLConnection加载网络数据并解析,下面就是怎么把数据显示到手机界面上的问题了。
既然我们用ListView控件,并且有图又有文字,所以,我们必须先自定义一个适配器MyAdapter继承BaseAdapter并且对ListView进行优化,代码很简单。另外,我们解析到的图片其实是接口给我们返回的url地址。加载图片,我们可以再去开启个异步任务加载图片,也可以使用SmartImageView开源视图,它支持通过url来加载图片,所以我们直接调用它的setImageUrl()方法,把图片url地址传进去就行了,很方便,而且它帮你把缓存的事都做了。
public class Myadapter extends BaseAdapter { private ArrayList<News> list; private Context context; public Myadapter(ArrayList<News> list, Context context) { this.list = list; this.context = context; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.news_item, parent, false); holder = new ViewHolder(); holder.title = (TextView) convertView.findViewById(R.id.title); holder.date = (TextView) convertView.findViewById(R.id.date); holder.author_name = (TextView) convertView.findViewById(R.id.author_name); // holder.thumbnail_pic_s = (SmartImageView) convertView.findViewById(R.id.imgsrc); holder.thumbnail_pic_s = (ImageView) convertView.findViewById(R.id.imgsrc); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.title.setText(list.get(position).getTitle()); holder.date.setText(list.get(position).getDate()); holder.author_name.setText(list.get(position).getAuthor_name()); //加载图片方式一:SmartImageView //holder.thumbnail_pic_s.setImageUrl(list.get(position).getThumbnail_pic_s()); //加载图片方式二:开启ImageTask异步加载图片 // 设置让iv在图片下载等待过程中显示提示图片 holder.thumbnail_pic_s.setImageResource(R.mipmap.ic_launcher); //通过setTag方法让iv上存储当前图片的下载网址 holder.thumbnail_pic_s.setTag(list.get(position).getThumbnail_pic_s()); new ImageTask(holder.thumbnail_pic_s).execute(list.get(position).getThumbnail_pic_s()); return convertView; } class ViewHolder{ TextView title; TextView date; TextView author_name; ImageView thumbnail_pic_s; //SmartImageView thumbnail_pic_s; } }
private void initAdapter() { adapter = new Myadapter(list, MainActivity.this); lv.setAdapter(adapter); } private void initData(String Url_News) { new NewsTask(this, MainActivity.this).execute(Url_News); } private void initView() { lv = (ListView) findViewById(R.id.listview); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public void getNews(ArrayList<News> result) { list.clear(); list.addAll(result); adapter.notifyDataSetChanged(); } }
所有的工作完成,编译运行OK。
总结:
1、Demo写的不是很完美,很多地方都可以进行完善,比如我们可以把AsyncTask完全封装起来,无论你加载什么网络数据都可以去调用。当然,网上有很多很多的开源框架加载网络数据。
2、当getView方法实现优化处理后,如果直接在此处实现图片下载,并且不等图片下载完成上下多次滚动列表后,容易出现图片乱闪的问题,解决方式如下:
通过setImageResource方法设置iv在下载过程中显示的提示图片
通过holder.iv调用setTag方法存储当前屏幕上显示的iv上对应的图片网址,启动异步任务下载图片
在异步任务中创建变量url,用于存储本次下载的图片网址
在异步任务的onPostExecute方法中判断本次下载图片的网址与iv中getTag的值是否相同,如果相同则显示图片