【Android 網路資料解析實現一個簡單的新聞例項(一)】
阿新 • • 發佈:2019-01-23
一般安卓在學到非同步任務AsyncTask之後都會有個安卓小專案的任務。得到(荔枝新聞,茶百科等)新聞網路介面來解析網路圖片或文字到ListView元件上顯示。其中要使用到的知識大概有:獲取網路資料(HttpUtil),解析網路資料(NewsParse),防止因解析超時應用程式無響應(ANR:Application
Not Responding)的非同步任務(AsyncTask),還有一個自定義的介面卡(NewsAdapter),還有就是例項化AsyncTask類傳遞路徑進行解析載入的MainActivity了。剩下的就是兩個xml了,一個是主方法的。一個是ListView的自定義佈局xml,。本次部落格就不講解點選ListView後加載詳情頁面了。
先上圖看看:
兩個佈局檔案:
activity_main.xml
<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=".MainActivity" > <TextView android:id="@+id/textView1" android:layout_width="fill_parent" android:layout_height="60dp" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="琦天下新聞" android:textColor="#ffffff" android:paddingTop="15dp" android:paddingLeft="110dp" android:background="#009999" android:textAppearance="?android:attr/textAppearanceLarge" /> <ListView android:id="@+id/listView1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#cccccc" android:layout_alignParentLeft="true" android:layout_below="@+id/textView1" > </ListView> </RelativeLayout>
listview_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="horizontal" android:layout_height="match_parent" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="120dp" android:background="#fff">
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher"
/>
<TextView
android:id="@+id/subject"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:textColor="#009966"
android:textSize="14sp"
android:layout_toRightOf="@+id/image"
android:text="TextView" />
<TextView
android:id="@+id/changed"
android:layout_width="fill_parent"
android:layout_height="20dp"
android:layout_alignParentBottom="true"
android:textColor="#999999"
android:layout_toRightOf="@+id/image"
android:text="TextView" />
<TextView
android:id="@+id/summary"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_above="@+id/changed"
android:layout_toRightOf="@+id/image"
android:text="TextView" />
</RelativeLayout>
</LinearLayout>
1,HttpUtil工具,專門用來網路獲取資料的。直接上程式碼。
這是HttpGet獲取的方式是一種特別簡單的方式。
public class HttpUtil {
public static byte[] getJsonString(String path) throws ClientProtocolException, IOException{
byte[] data = null;
HttpGet get = new HttpGet(path);
HttpClient client = new DefaultHttpClient();
HttpResponse response = null;
response = client.execute(get);
if(response.getStatusLine().getStatusCode()==200)
{
data = EntityUtils.toByteArray(response.getEntity());
}
return data;
}
再上一種獲取資料方式吧,兩種是一樣的都是byte的型別,這些都是為了同時載入圖片和使文字使用的方式,當然還有流的方式,但是我下面這個demo是將流的方式再轉成byte方式。
public class HttpUtil {
public static byte[] parseImage(String path){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setReadTimeout(5000);
connection.setDoInput(true);
connection.connect();
if(connection.getResponseCode() == 200){
InputStream inputStream = connection.getInputStream();
int temp = 0;
byte[] buffer = new byte[1024];
while ((temp = inputStream.read(buffer)) !=-1) {
outputStream.write(buffer, 0, temp);
outputStream.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
}
2,NewsParse解析獲取到的資料也稱JSON解析。
public class ParseTool {
public static List<News> parseNews(String json) throws JSONException{
List<News> list = new ArrayList<News>();
JSONObject object = new JSONObject(json);
object = object.getJSONObject("paramz"); //這個是獲取集合名的欄位“{ }”
JSONArray array = object.getJSONArray("feeds");//這個是獲取陣列名的欄位 “[ ]”
int len = array.length();
for(int i = 0 ; i <len; i ++){
object = array.getJSONObject(i);
object = object.getJSONObject("data");
String subject = object.getString("subject");
String summary = object.getString("summary");
String cover = object.getString("cover");
String changed = object.getString("changed");
News news = new News();
news.setSubject(subject);
news.setCover(cover);
news.setChanged(changed);
news.setSummary(summary);
list.add(news);
}
//這段用來檢視有沒有解析到
System.out.println("++++++++++++++++++++++++++++++++++++"+list);
return list;
}
}
3,自定義介面卡NewsAdapter,是為了讓ListView中的item是以自定義佈局的方式顯示的。而不是用ArrayAdapter的方式直接只顯示一條資料。
public class NewsAdapter extends BaseAdapter {
private List<News> list = null;
private Context context =null;
//private ViewHolder holder =null;
public NewsAdapter(Context context, List<News> list) {
// TODO Auto-generated constructor stub
this.context = context;
this.list = list;
}
@Override
public int getCount() {
int count = 0 ;
if(list!=null){
count = list.size();
}
return count;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null){
convertView = LayoutInflater.from(context).inflate(R.layout.item_person, parent,false);
ViewHolder holder = new ViewHolder();
holder.cover = (ImageView) convertView.findViewById(R.id.image);
holder.subject = (TextView) convertView.findViewById(R.id.subject);
holder.summary = (TextView) convertView.findViewById(R.id.summary);
holder.changed = (TextView) convertView.findViewById(R.id.changed);
convertView.setTag(holder);
}
final ViewHolder holder = (ViewHolder) convertView.getTag();
String cover = list.get(position).getCover();
String subject = list.get(position).getSubject();
String summary = list.get(position).getSummary();
String changed = list.get(position).getChanged();
String imageUrl = "http://litchiapi.jstv.com"+cover;
holder.subject.setText(subject);
holder.summary.setText(summary);
holder.changed.setText(changed);
holder.cover.setImageResource(R.drawable.ic_launcher);
//根據圖片的地址去下載圖片---用介面回撥解決
Bitmap bitmap = ImageUtil.readImage(imageUrl);//這裡還用到了圖片快取到擴充套件卡,方便圖片載入
if(bitmap!=null){
holder.cover.setImageBitmap(bitmap);
}else{
new DownImageTask(new DownImageTask.DownLoadBack() { //這兒用到了介面回撥,下面會講到
@Override
public void response(Bitmap bitmap) {
holder.cover.setImageBitmap(bitmap);
}
}).execute(imageUrl);
}
return convertView;
}
class ViewHolder{
TextView subject,changed,summary;
ImageView cover;
}
}
ImageUtil.java
package com.example.newsapp.tool;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;
/**
* 操作擴充套件卡中檔案的工具類(以快取下載的圖片為例
* 擴充套件卡對應的目錄:mut/sdcard
* @author Administrator
*
*/
public class ImageUtil {
//定義儲存圖片的目錄
public static final String IMAGE_URL = Environment.getExternalStorageState()+"/cache/images";
public static final int FORMAT_PNG =1;
public static final int FORMAT_JPEG =1;
/**
* 判斷擴充套件卡是否掛載
*
*/
public static boolean isMounted(){
String state = Environment.getExternalStorageState();//獲取擴充套件卡的狀態
return state.equals(Environment.MEDIA_MOUNTED);
}
/**
* 根據下載圖片的路徑獲取圖片的檔名
*
*/
public static String getFileName(String url){
return url.substring(url.lastIndexOf("/")+1);
}
/**
* 儲存圖片到擴充套件卡
* @throws IOException
*
*/
/* public static void saveImage(String url,byte[] data) throws IOException{
//判斷擴充套件是否掛載
if(!isMounted()){
return ;
}
File dir = new File(IMAGE_URL);
if(!dir.exists()){
dir.mkdir();
FileOutputStream fos = new FileOutputStream(new File(dir,getFileName(url)));
fos.write(data);
fos.close();
}
}
*/
/**
* 儲存圖片到擴充套件卡
* @throws IOException
*
*/
public static void saveImage(String url , Bitmap bitmap,int format) throws IOException{
if(!isMounted()){
return;
}
File dir = new File(IMAGE_URL);
if(!dir.exists()){
dir.mkdir();
}
FileOutputStream fos = new FileOutputStream(new File(dir,getFileName(url)));
bitmap.compress(format == 1? CompressFormat.PNG:CompressFormat.JPEG, 100, fos);
fos.close();
}
/**
* 從擴充套件卡獲取圖片
*/
public static Bitmap readImage(String url){
if(!isMounted()){
return null;
}
File file = new File(IMAGE_URL);
Bitmap bitmap = null;
if(file.exists()){
//根據圖片檔案路徑得到Bitmap型別的物件
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
/**
* 清空圖片快取中的圖片
*/
public static void clear(){
File dir = new File(IMAGE_URL);
if(dir.exists()){
File[] file = dir.listFiles();
for(File files : file){
files.delete();
}
}
}
/**
* 判斷擴充套件卡的剩餘空間
*/
public static long getSize(){
StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
int count = statFs.getFreeBlocks();//得到剩餘資料塊的個數
int size = statFs.getBlockCount();//得到每個資料塊的大小
long ssize = (count*size)/1024/1024;
return ssize;
}
}
今後這些工具類直接收藏起來,到時候要用的時候直接放入後呼叫就行。
4 AsyncTask非同步任務類
上面已經談到過一點了,在主執行緒中執行網路解析任務,響應時超過5秒,基本上會報一種叫ANR的錯誤。為了解決錯誤前期就用了非同步任務去解決,當然等後期學到框架的時候,這些東西只需要一兩個jar包就可以搞定。
NewsTask.java
package com.example.newsapp.asyncTask;
import java.io.IOException;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.json.JSONException;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.ListView;
import com.example.newsapp.entity.News;
import com.example.newsapp.newAdapter.NewsAdapter;
import com.example.newsapp.tool.HttpUtil;
import com.example.newsapp.tool.ParseTool;
public class NewsTask extends AsyncTask<String, Void, List<News>> {
private Context context;
private ListView listView;
public NewsTask(Context context, ListView listView) {
super();
this.context = context;
this.listView = listView;
}
@Override
protected List<News> doInBackground(String... params) {
// TODO Auto-generated method stub
String url = params[0];
List<News> list = null;
if(url!=null)
{
try {
byte[] data = HttpUtil.getJsonString(url);
String jsonString = new String(data,"utf-8");
list = ParseTool.parseNews(jsonString);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return list;
}
@Override
protected void onPostExecute(List<News> result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
NewsAdapter adapter = new NewsAdapter(context, result);
listView.setAdapter(adapter);
}
}
當然下載圖片也得用非同步任務,有的圖片5秒可下載不來的。
DownloadImageTask.java
package com.example.newsapp.asyncTask;
import java.io.IOException;
import org.apache.http.client.ClientProtocolException;
import com.example.newsapp.tool.HttpUtil;
import com.example.newsapp.tool.ImageUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
public class DownImageTask extends AsyncTask<String, Void, Bitmap> {
private DownLoadBack downLoadBack;
public DownImageTask(DownLoadBack downLoadBack)
{
this.downLoadBack = downLoadBack;
}
@Override
protected Bitmap doInBackground(String... params) {
String url = params[0];
Bitmap bitmap = null;
if(url!=null)
{
try {
byte[] data = HttpUtil.getJsonString(url);
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//圖片下載完成時,把圖片存到擴充套件卡
ImageUtil.saveImage(url, bitmap, ImageUtil.FORMAT_JPEG);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
downLoadBack.response(result);
}
//在這裡還用到介面回撥,很多人都不理解介面回撥是什麼?當然在這兒只不過是用來傳值而已。呼叫者需要被呼叫者的資料,那麼被呼叫者就定義了一個介面(含方法),呼叫者在例項被呼叫者的時候,通過介面回撥並實現介面中的方法,就可以得到被呼叫者的資料了。
public interface DownLoadBack
{
public void response(Bitmap bitmap);
}
}
當然我這麼說都不懂的,就看看:介面回撥機制的詳解
5 那就是主方法中要實現的了。MainActivity.java
package com.example.newsapp;
import com.example.newsapp.asyncTask.NewsTask;
import com.example.newsapp.entity.News;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
private String path = "http://litchiapi.jstv.com/api/GetFeeds?column=0&PageSize=10&pageIndex=1&val=100511D3BE5301280E0992C73A9DEC41"; //這裡就是地址了
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView1);
new NewsTask(this, listView/**這裡傳入的引數,與非同步任務類建構函式的形參一致,位置別寫反了*/).execute(path); //這裡執行了非同步任務的方法,並傳入路徑。
listView.setOnItemClickListener(new OnItemClickListener() { //這裡我使用了一個點選ListView的監聽,這兒一般是點選後就會跳轉並顯示該條目的詳情,在這片文章中我還完善。
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String item = (String) listView.getItemAtPosition(position).toString();
Toast.makeText(getApplicationContext(), item, Toast.LENGTH_LONG).show();
}
});
}
}
6 當然最後還得在清單檔案(AndroidManifest.xml)中加上幾個許可權了。 <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
哈哈,在這兒,基本就完成了ListView顯示資料。