1. 程式人生 > >【Android 網路資料解析實現一個簡單的新聞例項(一)】

【Android 網路資料解析實現一個簡單的新聞例項(一)】

      一般安卓在學到非同步任務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顯示資料。