您的位置:首页 > 运维架构 > 网站架构

从网站解析JSON异步加载到ListView事例

2016-03-08 16:05 671 查看
从http://www.imooc.com/learn/406学习到「从网站解析JSON异步加载到ListView」这样一个例子。整理如下。

主要知识点包括:异步(子线程)下载图片、图片的缓存、JSON解析

最终列表效果如图



主布局就是一个ListView,没什么好说的。

然后是每个子View的布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">

<ImageView
android:id="@+id/image_view"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_launcher" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
android:paddingLeft="5dp">

<TextView
android:id="@+id/title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Title"
android:textSize="15sp" />

<TextView
android:id="@+id/content_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:text="content"
android:textSize="10sp" />

</LinearLayout>
</LinearLayout>


默认左边一张android机器人小图,右边标题和内容

public class MainActivity extends AppCompatActivity {

private ListView listView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list_view);
String URL = "http://www.imooc.com/api/teacher?type=4&num=30";
/*开启异步进程*/
new newsAsyncTask().execute(URL);
}

class newsAsyncTask extends AsyncTask<String, Void, List<NewsBean>> {

@Override
protected List<NewsBean> doInBackground(String... params) {
String url = params[0];
List<NewsBean> newsBeanList = new ArrayList<>();
NewsBean newsBean;
try {
/*打开url对应的输入流,用readStream方法得到String格式的数据*/
String jsonString = readStream(new URL(url).openStream());
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
/*每个JSONArray里的一项都是一个包含图片、标题、内容的JSONObject对象,依次把他们存入
* 一个封装的newsBean数组中*/
jsonObject = jsonArray.getJSONObject(i);
newsBean = new NewsBean();
newsBean.newsImageUrl = jsonObject.getString("picSmall");
newsBean.newsTitle = jsonObject.getString("name");
newsBean.newsContent = jsonObject.getString("description");
newsBeanList.add(newsBean);
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return newsBeanList;
}

@Override
protected void onPostExecute(List<NewsBean> newsBeans) {
super.onPostExecute(newsBeans);
NewsArrayAdapter newsArrayAdapter = new NewsArrayAdapter(MainActivity.this,
R.layout.item_layout, newsBeans);
listView.setAdapter(newsArrayAdapter);
}
}

private String readStream(InputStream inputStream) {
String result = "";
try {
String line;
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((line = bufferedReader.readLine()) != null) {
result += line;
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}


MainActivity中的url是一个慕课网的API



可以看到整个的首先是一个Object,点击展开



我们需要的是这个名为“data”的JSONArray



遍历JSONArray中的每个JSONObject,我们需要的就是里面的数据。把他们存进一个NewsBean封装类里

public class NewsBean {
public String newsImageUrl;
public String newsTitle;
public String newsContent;
}


完成后把List<NewsBean> newsBeans传入我们自定义的Adapter中,加载到ListView里显示出来。

那么我们具体的去看看这个自定义的Adapter

public class NewsArrayAdapter extends ArrayAdapter<NewsBean> {

private int resource;
private List<NewsBean> objects;
private ImageLoader imageLoader;

public NewsArrayAdapter(Context context, int resource, List<NewsBean> objects) {
super(context, resource, objects);
this.resource = resource;
this.objects = objects;
imageLoader = new ImageLoader();
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;

if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resource, null);
/*viewHolder用来对控件实例进行缓存,避免每次通过findViewById()获取控件*/
viewHolder = new ViewHolder();
viewHolder.titleView = (TextView) view.findViewById(R.id.title_text);
viewHolder.contentView = (TextView) view.findViewById(R.id.content_text);
viewHolder.imageView = (ImageView) view.findViewById(R.id.image_view);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.titleView.setText(objects.get(position).newsTitle);
viewHolder.contentView.setText(objects.get(position).newsContent);
String imageUrl = objects.get(position).newsImageUrl;
/*每个imageView设置独立对应的标签(对应的url),避免上下滑动listView时,
缓存中的图片加载显示到不对应的imageView中*/
viewHolder.imageView.setTag(imageUrl);
/*通过自定义的imageLoader类设置图片*/
imageLoader.setImageByThread(viewHolder.imageView, imageUrl);
return view;
}

class ViewHolder {
TextView titleView;
TextView contentView;
ImageView imageView;
}
}


ListView中经典的利用convertView和ViewHolder避免多次加载的技巧我们当然用到了,我们还用到了一个自定义的类,用这个类中的方法去加载图片。那我们去看看这个类。

public class ImageLoader {

private ImageView imageView;
private String imageUrl;
private LruCache<String, Bitmap> mCache;

public ImageLoader() {
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 4;
/*用来缓存图片的内存,设置大小为最大可用内存的1/4*/
mCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
/*默认返回的是数量,我们重写让它返回每个bitmap的大小*/
return value.getByteCount();
}
};
}

private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
/*当imageView与设置的Tag配对时才加载图片*/
if (imageView.getTag().equals(imageUrl)) {
imageView.setImageBitmap((Bitmap) msg.obj);
}
}
};

public void setImageByThread(ImageView imageView, final String newsImageUrl) {
this.imageView = imageView;
imageUrl = newsImageUrl;
new Thread() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(newsImageUrl);
Message message = Message.obtain();
message.obj = bitmap;
handler.sendMessage(message);
}
}.start();
}

public Bitmap getBitmapFromURL(String newsImageUrl) {
Bitmap bitmap = getBitmapFromCache(newsImageUrl);
/*如果内存中已有缓存的此图,直接返回此图*/
if (bitmap != null) {
return bitmap;
}
InputStream inputStream = null;
try {
URL url = new URL(newsImageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
inputStream = new BufferedInputStream(connection.getInputStream());
bitmap = BitmapFactory.decodeStream(inputStream);
connection.disconnect();
if (bitmap != null) {
addBitmapToCache(newsImageUrl, bitmap);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

public void addBitmapToCache(String imageUrl, Bitmap bitmap) {
if (getBitmapFromCache(imageUrl) == null) {
mCache.put(imageUrl, bitmap);
}
}

public Bitmap getBitmapFromCache(String imageUrl) {
return mCache.get(imageUrl);
}
}


值得注意的是,我们之前在Adapter中已经对每个imageView设置了特定的Tag,在这里就用到了这个判断,保证不会因为imageView的复用出现图片多次加载的情况。至此整个demo就基本完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  listview 异步 json