> Hello World !!!

     

@syaku

안드로이드 스레드 XML 리스트 뷰 (1) : Android XML ListView Adapter Http AsyncTask


written by Seok Kyun. Choi. 최석균

"안드로이드 XML 네트워크 리스트 뷰"


만년만에 포스팅... 잠시 개인 프로젝트 개발에 열중하다보니 시간이 없었습니다.

최근 다시 안드로이드를 개발하고 있으며, 개발 중 테스트용으로 개발된 정보를 정리하여 포스팅하였습니다.

이번 포스팅은 XML 을 HTTP 프로토콜로 호출하여 파싱 후 리스트뷰 출력하는 부분까지 작성되었습니다.



* 개발환경


- 안드로이드 버전

minSdkVersion : 16 (Android 4.1.2)

targetSdkVersion 18 (Android 4.3)


- 개발 플렛폼

Mac OS X 10.8.5

ADT (Android Developer Tools Build: v22.2.1-833290)



* 알아두기


- 안드로이드 네트워크를 사용하려면 인터넷 권한을 활성화해야 합니다.

오류 메세지 : java.lang.SecurityException: Permission denied (missing INTERNET permission?)


AndroidManifest.xml 에서 <uses-permission android:name="android.permission.INTERNET"/> 추가해야 합니다.

문제점 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202037499

퍼미션 설정 참고 : http://ljsk139.blog.me/30177948090


- 또한 네트워크를 이용할때는 꼭 스레드에서 작업을 해야합니다. 그렇지 않을 경우 오류가 발생합니다.

오류 메세지 : android.os.NetworkOnMainThreadException

참고 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202037396


- 안드로이드 에뮬레이터 AVDM 한글문제

4.2.x 와 4.3 에서 한글을 지원하지 않습니다. 언제 업그래이드 될지는 모르나... 현재는 그렇더군요.

그래서 최소 버전을 16 대상 버전을 18로 설정하고 개발하였습니다.

문제점 : http://tweakerz.blog.me/40198483134

한글폰트 추가하기 : http://jpub.tistory.com/336



- resource 는 첨부파일을 참조하고 자바 소스만 포스팅하겠습니다. (XmlBean 클래스도 생략합니다)


* 소스압축 파일


SyakuXmlApp.zip



결과화면




HTTP 프로토콜 클래스


Apache HttpComponents 를 사용하였습니다.

http://hc.apache.org/


특정 url 에 접속하여 결과를 InputStream 받아서 반환하는 작업을 담당합니다.


/*
 * HttpConnection.java
 *
 * Copyright (c) 2013, Seok Kyun. Choi. (최석균)
 * http://syaku.tistory.com
 * 
 */


package com.syaku;

import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.util.Log;

public class HttpConnection {
	String url = null;
	HttpResponse response = null;
	HttpEntity entity = null;
	InputStream input_stream = null;
	
	public HttpConnection(String url) {
		this.url = url;
		
		Log.i("HttpConnection RUN",url);
		
		try {
			HttpGet request = new HttpGet(url);
			HttpClient httpclient = new DefaultHttpClient();
			this.response = httpclient.execute(request);
			this.entity = this.response.getEntity();
			this.input_stream = this.entity.getContent();
			
		} catch (Exception e) {
			Log.e("HttpConnection ERROR",e.toString());
		}
	}
	
	void setEntity(HttpEntity entity) {
		this.entity = entity;
	}
	
	public HttpResponse getResponse() {
		return this.response;
	}
	public HttpEntity getEntity() {
		return this.entity;
	}
	public InputStream getInputStream() {
		return this.input_stream;
	}

}


Xml 파서 클래스


안드로이드에서 제공하는 org.w3c.dom 을 사용하였습니다.

ArrayList 에 HashMap 을 사용하지 않고 SparseArray 사용하였습니다.

안드로이드에서는 SparseArray를 권장합니다.

참고 : http://www.mnworld.co.kr/1638


기본적인 xml 데이터 구조

<data>

 <item>

   <a> ...     <- get(0)

   <b> ...     <- get(1)

   ...           <- get(x)

 </item>

 <item>

   <a> ...     <- get(0)

   <b> ...     <- get(1)

   ...           <- get(x)

 </item>

</data>


InputStream 자료를 읽어 ArrayList 로 반환하는 작업을 담당합니다.


/*
 * Xml2Object.java
 *
 * Copyright (c) 2013, Seok Kyun. Choi. (최석균)
 * http://syaku.tistory.com
 * 
 */

package com.syaku;

import java.io.InputStream;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import android.util.Log;
import android.util.SparseArray;

public class Xml2Object {
	Document doc = null;

	public Xml2Object(InputStream input_stream) {
		try {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		
		this.doc = builder.parse(input_stream);
		} catch (Exception e) {
		
		}
	}
	
	public ArrayList<SparseArray<String>> list(String name) {
		ArrayList<SparseArray<String>> list = new ArrayList<SparseArray<String>>();
		
		try {
			
		Element root = doc.getDocumentElement();
		NodeList node_list = root.getElementsByTagName(name);
		
		if (node_list == null) {
			return list;
		}
		
		int count = node_list.getLength();
		Log.d("Node List Count",""+count);
		
		for (int i = 0; i < count; i++) {
			NodeList items = node_list.item(i).getChildNodes();
			int item_count = items.getLength();
			
			SparseArray<String> data = new SparseArray<String>();
			int key = 0;
			for (int x = 0; x < item_count; x++) {
				
				Node node = items.item(x);
				if (node != null) {
					//String node_name = node.getNodeName();
					String node_value = "";
					Node node_text = node.getFirstChild();
					if (node_text != null) {
						node_value = node_text.getNodeValue();
					}
					
					data.append(key, node_value);
					//Log.d(key + ") Item Node name",node_name);
					//Log.d(key + ") Item Node value",node_value);
					
					key++;
				
				}
			}
			
			if (data != null) {
				list.add(data);
			}
		}
		
		Log.d("list data add",list.size() + "ea success");
		
		} catch (Exception e) {
			Log.e("list Error",e.toString());
		}
		
		return list;
	}
	
	public SparseArray<String> object(String name) {
		SparseArray<String> data = new SparseArray<String>();
		
		NodeList node_list = doc.getElementsByTagName(name);
		
		if (node_list == null) {
			return data;
		}
		
		NodeList items = node_list.item(0).getChildNodes();
		int count = items.getLength();
		Log.d("Item Node Count",""+count);
		
		int key = 0;
		for (int i = 0; i < count; i++) {
			Node node = items.item(i);
			
			if (node != null) {
				//String node_name = node.getNodeName();
				String node_value = "";
				Node node_text = node.getFirstChild();
				if (node_text != null) {
					node_value = node.getFirstChild().getNodeValue();
				}
				//Log.d("Item Node name",node_name);
				//Log.d("Item Node value",node_value);
				data.append(key, node_value);
				key++;
			}
		}
		
		return data;
	}
}


메인 액티비티 클래스


모든 작업을 호출하고 결과를 출력하는 작업을 담당합니다.


네트워크로 작업할때는 꼭 스레드를 사용해야 합니다. 

멀티테스킹 AsyncTask 클래스를 사용했습니다.

참고 : http://blog.naver.com/jaejae1988?Redirect=Log&logNo=60202038678


XmlListAsyncTask 클래스는 스레드를 이용하여 Xml 데이터를 불러오는 작업을 담당합니다.


package com.syaku;

import java.io.InputStream;
import java.util.ArrayList;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.widget.ListView;
import android.widget.TextView;
import android.app.Activity;
import android.content.Context;

public class SyakuMain extends Activity {
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.syaku_main);
		
		// XML 데이터 호출
		XmlListAsyncTask xml = new XmlListAsyncTask(this);
		String url = "http://xxx.xml";
		xml.execute(url);
	}
	
	// AsyncTask 스레
	class XmlListAsyncTask extends AsyncTask< String, Void , ArrayList<SparseArray<String>> > {
		Context context;
		ArrayList<XmlBean> list_item;
		
		public XmlListAsyncTask(Context context) {
			this.context = context;
		}
		
		@Override
		protected ArrayList<SparseArray<String>> doInBackground(String... params) {
			ArrayList<SparseArray<String>> list = new ArrayList<SparseArray<String>>();
			
			try {
				
				// xml 호
				String url = params[0];
				HttpConnection http = new HttpConnection(url);
				InputStream input_stream = http.getInputStream();
				
				//xml 파
				Xml2Object xml = new Xml2Object(input_stream);
				list = xml.list("item");
			} catch (Exception e) {
				Log.e("GET ERROR",e.toString());
			}
			
			return list;
		}
		
		@Override
		protected void onPostExecute(ArrayList<SparseArray<String>> result) {
			super.onPostExecute(result);
			
			int count = result.size();
			Log.d("ArrayList count",""+count);
			
			TextView tv = (TextView) findViewById(R.id.list_count);
			tv.setText(""+count);
			
			list_item = new ArrayList<XmlBean>();

			for (int i = 0; i < count; i++) {
				SparseArray<String> data = result.get(i);
				
				XmlBean bean = new XmlBean();
				
				String title = data.get(bean.TITLE);
				String regdate = data.get(bean.REGDATE);
				
				bean.setTitle(title);
				bean.setRegdate(regdate);
				
				list_item.add(bean);
			}
			
			XmlListAdapter adapter = new XmlListAdapter(context, R.layout.list, list_item);
			ListView listView = (ListView) findViewById(R.id.list_view);
			listView.setAdapter(adapter);
		}
		
	}
	
}




XmlListAdapter 클래스


ArrayList 자료를 받아 뷰에 목록 형식으로 출력 작업을 담당합니다.

하나의 항목이 목록으로 출력되는 형식이 아닌 다양한 항목이 하나의 목록에 출력될 수 있게 작업된 구조입니다.

아래의 클래스는 제목과 날짜를 항목을 출력합니다.


package com.syaku;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class XmlListAdapter extends BaseAdapter {
	Context context;
	LayoutInflater inflater;
	ArrayList<XmlBean> bean;
	int layout;
	
	public XmlListAdapter(Context context, int layout, ArrayList<XmlBean> bean) {
		this.context = context;
		this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		this.layout = layout;
		this.bean = bean;
		
	}
	
        @Override
	public int getCount() {
		return bean.size();
	}
	
        @Override
	public String getItem(int position) {
		return null;
	}
	
        @Override
	public long getItemId(int position) {
		return position;
	}
	
        @Override
	public View getView(int postion, View view, ViewGroup view_group) {
		final int pos = postion;
		
		if (view == null) {
			view = inflater.inflate(layout,view_group,false);
		}
		
		TextView tv;
		
		tv = (TextView) view.findViewById(R.id.title);
		tv.setText(bean.get(pos).title);
		
		tv = (TextView) view.findViewById(R.id.regdate);
		tv.setText(bean.get(pos).regdate);
		
		return view;
	}
}



다음 포스팅은 목록을 클릭하여 해당 자료 상세보기 액티비티로 전환되는 과정을 설명하겠습니다.


2013/10/28 - [개발노트/안드로이드 SDK] - 안드로이드 스레드 XML 뷰 와 웹 이미지 뷰 (2) : Android XML Http AsyncTask ImageView Intent




posted syaku blog

Syaku Blog by Seok Kyun. Choi. 최석균.

http://syaku.tistory.com