制作一个简易的新闻客户端,主要目的是学习网络数据解析及异步加载,目前只包括从网页获取数据生成列表功能,未添加点击列表显示新闻内容功能,
类文件
MainActivity
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ListView mListView;
//初始化网址
private static String urlString = "http://www.imooc.com/api/teacher?type=4&num=30";
private Context mContext = this;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.lv_main);
new NewsAsyncTask().execute(urlString);
}
/**
* 实现网络的异步访问
*/
class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>>{
@Override
protected List<NewsBean> doInBackground(String... strings) {
//从网页获取数据
return getJsonData(strings[0]);
}
@Override
protected void onPostExecute(List<NewsBean> newsBeans) {
super.onPostExecute(newsBeans);
NewsAdapter mNewsAdapter = new NewsAdapter(mContext, newsBeans, mListView);
mListView.setAdapter(mNewsAdapter);
}
}
/**
* 将url对应的JSON格式数据转化为NewsBean对象
* @param url
* @return
*/
private List<NewsBean> getJsonData(String url) {
List<NewsBean> newsBeanList = new ArrayList<>();
try {
String jsonString = readStream(new URL(url).openStream());
JSONObject jsonObject;
NewsBean newsBean;
try {
jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");
for (int i=0; i<jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
newsBean = new NewsBean();
newsBean.newsIconUrl = jsonObject.getString("picSmall");
newsBean.newsTitle = jsonObject.getString("name");
newsBean.newsContent = jsonObject.getString("description");
newsBeanList.add(newsBean);
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return newsBeanList;
}
/**
* 通过inputstream解析网页返回的数据
* @param is
* @return
*/
private String readStream(InputStream is){
InputStreamReader isr;
String result = "";
try {
String line = "";
isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
while((line = br.readLine()) != null){
result += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
新闻实体类
public class NewsBean {
public String newsIconUrl;
public String newsTitle;
public String newsContent;
}
列表适配器
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.List;
/**
* Created by HPF on 2016/6/10.
*/
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{
private Context mContext;
private List<NewsBean> mList;
private LayoutInflater mInflater;
private ImageLoader mImageLoader;
private int mStart, mEnd;
public static String[] URLs;
private boolean mFirstIn;
/**
* 构造函数
* @param context
* @param list
* @param listView
*/
public NewsAdapter(Context context, List<NewsBean> list, ListView listView) {
mContext = context;
mList = list;
mInflater = LayoutInflater.from(mContext);
//初始化图片加载器
mImageLoader = new ImageLoader(listView);
URLs = new String[list.size()];
mFirstIn = true;
for (int i=0; i<list.size(); i++){
URLs[i] = list.get(i).newsIconUrl;
}
listView.setOnScrollListener(this);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int i) {
return mList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder = null;
if (view == null){
view = mInflater.inflate(R.layout.item_layout, null);
viewHolder = new ViewHolder(view);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);
viewHolder.ivIcon.setTag(mList.get(i).newsIconUrl);
// new ImageLoader().showImageByThread(viewHolder.ivIcon, mList.get(i).newsIconUrl);
//异步加载图片
mImageLoader.showImageByAsyncTask(viewHolder.ivIcon, mList.get(i).newsIconUrl);
viewHolder.tvTitle.setText(mList.get(i).newsTitle);
viewHolder.tvContent.setText(mList.get(i).newsContent);
return view;
}
/**
* 滑动停止时加载
* @param absListView
* @param i
*/
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
if (i == SCROLL_STATE_IDLE) {
mImageLoader.loadImage(mStart, mEnd);
} else {
mImageLoader.cancelAllTasks();
}
}
/**
* 只加载显示区域内的图片
* @param absListView
* @param i
* @param i1
* @param i2
*/
@Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
mStart = i;
mEnd = i + i1;
if (mFirstIn && i1>0) {
mImageLoader.loadImage(mStart, mEnd);
mFirstIn = false;
}
}
class ViewHolder{
public TextView tvTitle;
public TextView tvContent;
public ImageView ivIcon;
public ViewHolder(View view) {
tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvContent = (TextView) view.findViewById(R.id.tv_content);
ivIcon = (ImageView) view.findViewById(R.id.imv_icon);
}
}
}
图片异步加载
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
/**
* Created by HPF on 2016/6/10.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private ListView mListView;
private Set<NewsAsyncTask> mTask;
private LruCache<String, Bitmap> mCaches;
/**
* 构造函数
* @param listView
*/
public ImageLoader(ListView listView) {
//获取最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory/4;
//初始化缓存
mCaches = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
mListView = listView;
mTask = new HashSet<>();
}
/**
* 从缓存获取图片
* @param url
* @return
*/
private Bitmap getBitmapFromCache(String url) {
return mCaches.get(url);
}
/**
* 将图片加入到缓存
* @param url
* @param bitmap
*/
private void addBitmapToCache(String url, Bitmap bitmap) {
if (getBitmapFromCache(url)==null){
mCaches.put(url, bitmap);
}
}
/**
* 从网络获取图片
* @param urlString
* @return
* @throws IOException
*/
public Bitmap getBitmapFromURL(String urlString) throws IOException {
Bitmap bitmap = null;
InputStream is = null;
try {
URL url =new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(conn.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
is.close();
}
return bitmap;
}
/**
* 显示图片
* @param imageView
* @param url
*/
public void showImageByAsyncTask(ImageView imageView, String url){
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null) {
imageView.setImageResource(R.mipmap.ic_launcher);
} else {
imageView.setImageBitmap(bitmap);
}
}
public void cancelAllTasks() {
if (mTask != null){
for (NewsAsyncTask task:mTask){
task.cancel(false);
}
}
}
private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
private String mUrl;
public NewsAsyncTask(String url) {
this.mUrl = url;
}
Bitmap bitmap = null;
@Override
protected Bitmap doInBackground(String... strings) {
try {
bitmap = getBitmapFromURL(strings[0]);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap != null) {
addBitmapToCache(strings[0], bitmap);
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
if (imageView!=null&&bitmap!=null){
imageView.setImageBitmap(bitmap);
}
mTask.remove(this);
}
}
public void loadImage(int start, int end) {
for (int i=start; i<end; i++){
String url = NewsAdapter.URLs[i];
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap == null) {
NewsAsyncTask task = new NewsAsyncTask(url);
task.execute(url);
mTask.add(task);
} else {
ImageView imageView = (ImageView) mListView.findViewWithTag(url);
imageView.setImageBitmap(bitmap);
}
}
}
}
布局文件
列表项布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imv_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:src = "@mipmap/ic_launcher"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="4dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:maxLines="1"
android:text="Title"/>
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="10sp"
android:maxLines="3"
android:text="Content"/>
</LinearLayout>
</LinearLayout>
主布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hpf.newsdemo.MainActivity">
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!" />
</RelativeLayout>
根据慕课网教学视频制作