- 작성자 : 고덕한(deokhan.koh@gmail.com)
- 소속 : 코아모델링(www.coremodeling.com)
- 작성일자 : 2011년 9월 15일
안드로이드 핸드폰에서 Naver Image 를 읽어와서 어플리케이션에 보여주는 기능을 구현한다.
이전 자료에서는 News 목록을 보여지도록 처리했지만, 이번에는 이미지를 함께 보여주는 기능을 구현해본다.
Image 처리는 News 보다 좀 더 복잡한 구조를 가지고 있다.
- 네이버 계정으로 Open API 접속
네이버의 Open API 를 사용하여 뉴스, 이미지, 블로그 등등을 조회하기 위해서는 우선 Naver 에 회원가입이 되어있어야 하고, 네이버에 로그인을 해야 한다.
Open API 는 누구든지 key 를 발급 받으면 사용할 수 있으며, 일일동안에 접속하는 데이터량의 제한이 있다.
아래 이미지는 네이버 개발자 센터 홈페이지이다.
- Open API 확인
위 이미지에서 검색 API 메뉴를 클릭하면 검색 API 목록이 나온다. 검색 API 목록 중에서 이번 예제에서는 News 와 Image 목록을 안드로이드에서 조회하는 어플리케이션을 작성하려고 한다.
우선 검색을 하기 위해서는 네이버 개발사 사이트에서 키를 발급받아야 한다. 키를 발급받는 링크는 왼쪽 메뉴에 “키 등록/관리” 메뉴를 클릭한다.
검색 API 에 대한 key 와 지도 API key 두 가지 형태의 key 를 발급받을 수 있으며, 지도 API key 를 사용하고자 한다면, “키 추가” 버튼을 클릭하여 키를 추가한다.
아래 화면은 필자가 사용하는 key 를 발급 받은 내역이다.
이 XML 문서를 Parsing 하여 Andorid Application 에서 목록(ListView)으로 보여주려고 한다.
- XML Parsing
Android 에서는 XML Parsing 하는데 있어, 크게 3 가지 형태를 사용하고 있다.
3 가지 예제에 대해서는 앞에서 작성한 News Feed 예제를 참고하길 바란다.
https://docs.google.com/document/pub?id=1aHv43x9oYxL8dUCAtJ1ieYScyDPoQY9ErhyhSWo_Qz8
- Project 생성
우선 Project 를 먼저 생성하여, Application 을 개발하기 위한 초기 작업을 수행한다.
Eclipse 를 실행하고, File -> New -> Android Project 메뉴를 선택한다.
(기존에 News Feed 프로젝트를 생성하였으며, 그대로 패키지만 추가하여 사용하면 된다.)
- Project name : NaverApi
- Build Target : Android 2.3.3
- Package : com.coremodeling.naver
- Image 클래스 생성
하나의 Image 를 저장하는 Image 클래스를 생성한다. Open API 를 사용하여, XML 데이터를 Parsing 하게 되면, 각각의 Image 의 정보를 저장하기 위한 객체가 필요하다. 하나의 Image 를 저장하기 위한 객체를 만들기 위해서 Image 라는 이름의 클래스를 생성한다. 이 클래스에는 Naver 에서 Image 의 정보를 제공해주는 각 항목을 저장하기 위한 멤버변수가 존재하게 된다.
위의 XML 데이터에서 확인했듯이, 하나의 Image 에 대한 여러가지 내용을 저장하기 위한 멤버변수를 정의한다. Title, Link, Thumbnail, Sizeheight, Sizewidth 등이 기본적으로 저장된다.
따라서 아래와 같이 Image 클래스를 생성한다.
package com.coremodeling.naver.image;
import java.net.MalformedURLException; import java.net.URL;
public class Image implements Comparable<Image> {
static final String CHANNEL = "channel"; static final String ITEM = "item"; static final String TITLE = "title"; static final String LINK = "link"; static final String THUNMNAIL = "thumbnail"; static final String SIZE_HEIGHT = "sizeheight"; static final String SIZE_WIDTH = "sizewidth";
private String title; private URL link; private String thumbnail; private int sizeheight; private int sizewidth;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public URL getLink() { return link; }
public void setLink(String link) { try { if (link != null && link.trim().length() > 0) { this.link = new URL(link); } } catch (MalformedURLException e) { throw new RuntimeException(e); } }
public String getThumbnail() { return thumbnail; }
public void setThumbnail(String thumbnail) { this.thumbnail = thumbnail; }
public int getSizeheight() { return sizeheight; }
public void setSizeheight(int sizeheight) { this.sizeheight = sizeheight; }
public int getSizewidth() { return sizewidth; }
public void setSizewidth(int sizewidth) { this.sizewidth = sizewidth; }
public int compareTo(Image another) {
if (another == null) { return 1; }
return another.thumbnail.compareTo(thumbnail); }
}
|
ImageFeedParser Interface
Android 에서 XML 종류별로 XML Pasring 하는 내용을 살펴볼 것이다. 우선 XML 데이터를 Parsing 하기 위한 Interface 를 정의한다. 3 가지 Parser 에서 공통으로 사용되어질 메소드를 Interface 에서 미리 정의한다.
package com.coremodeling.naver.image;
import java.util.List;
public interface ImageFeedParser {
public List<Image> parse(); } |
BaseImageFeedParser Abstract Class
서버와 접속하여, XML 데이터를 얻은 다음, InputStream 으로 데이터를 받는 역할을 수행한다. 3 가지 Parser 가 XML 데이터를 받는 작업을 수행한다.
package com.coremodeling.naver.image;
import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL;
public abstract class BaseImageFeedParser implements ImageFeedParser {
final URL feedUrl;
protected BaseNewsFeedParser(String feedUrl){ try { this.feedUrl = new URL(feedUrl); } catch (MalformedURLException e) { throw new RuntimeException(e); } }
protected InputStream getInputStream() { try { return feedUrl.openConnection().getInputStream(); } catch (IOException e) { throw new RuntimeException(e); } }
}
|
ImageRssHandler 클래스
SAXParsing 을 하기 위한 DefaultHandler 클래스를 상속받는 RssHandler 클래스를 정의한다.
XML 데이터를 받고 나서, XML 데이터를 처음부터 끝까지 데이터를 읽으면서 Parsing 하는 작업을 수행한다.
Image 목록을 저장하기 위한 ImageList 와 하나의 News 를 저장하기 위한 Image 클래스, 그리고 문자열 작업을 위한 StringBuilder 클래스를 사용하여, XML 데이터를 Parsing 한다.
package com.coremodeling.naver.image;
import java.util.ArrayList; import java.util.List;
import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler;
public class ImageRssHandler extends DefaultHandler {
private List<Image> imageList; private Image currentImage; private StringBuilder builder;
public List<Image> getImageList(){ return this.imageList; }
@Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); builder.append(ch, start, length); }
@Override public void endElement(String uri, String localName, String name) throws SAXException { super.endElement(uri, localName, name);
if (this.currentImage != null){ if (localName.equalsIgnoreCase(Image.TITLE)){ currentImage.setTitle(builder.toString()); } else if (localName.equalsIgnoreCase(Image.LINK)){ currentImage.setLink(builder.toString()); } else if (localName.equalsIgnoreCase(Image.THUNMNAIL)){ currentImage.setThumbnail(builder.toString()); } else if (localName.equalsIgnoreCase(Image.SIZE_HEIGHT)){ currentImage.setSizeheight(Integer.parseInt(builder.toString())); } else if (localName.equalsIgnoreCase(Image.SIZE_WIDTH)){ currentImage.setSizewidth(Integer.parseInt(builder.toString())); } else if (localName.equalsIgnoreCase(Image.ITEM)){ imageList.add(currentImage); } } builder.setLength(0); }
@Override public void startDocument() throws SAXException { super.startDocument(); imageList = new ArrayList<Image>(); builder = new StringBuilder(); }
@Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { super.startElement(uri, localName, name, attributes); if (localName.equalsIgnoreCase(Image.ITEM)){ this.currentImage = new Image(); } } } |
SaxImageFeedParser 클래스
SaxParser 를 사용하여 XML 데이터를 Parsing 하는 클래스
package com.coremodeling.naver.image;
import java.util.List;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory;
public class SaxImageFeedParser extends BaseImageFeedParser {
public SaxImageFeedParser(String feedUrl){ super(feedUrl); }
public List<Image> parse() {
SAXParserFactory factory = SAXParserFactory.newInstance();
ImageRssHandler handler = new ImageRssHandler();
try {
SAXParser parser = factory.newSAXParser();
parser.parse(this.getInputStream(), handler);
} catch (Exception e) { e.printStackTrace(); }
return handler.getImageList(); }
}
|
- 화면 디자인
Image 를 검색하고, 검색한 결과를 목록으로 화면에 나와야 하기 때문에, 검색하기 위한 입력창과 버튼, 그리고 목록을 처리할 수 있는 화면을 디자인한다.
아래와 같이 /res/layout/newslist.xml 파일을 생성하고, XML 파일을 디자인한다.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/relativeLayout1"> <Button android:layout_height="wrap_content" android:onClick="onClickSearch" android:layout_width="wrap_content" android:text="검색" android:id="@+id/btnSearch" android:layout_alignParentTop="true" android:layout_alignParentRight="true"></Button> <EditText android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/editImageSearch" android:layout_toLeftOf="@id/btnSearch" android:layout_alignParentTop="true" android:layout_alignParentLeft="true"> <requestFocus></requestFocus> </EditText> </RelativeLayout> <ListView android:layout_height="wrap_content" android:id="@android:id/list" android:layout_width="fill_parent"></ListView> </LinearLayout> |
그리고 목록의 각 행을 디자인하기 위한 XML 파일을 /res/layout/image_row.xml 파일을 생성하고 아래와 같이 디자인한다.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:layout_width="50dip" android:id="@+id/imageView1" android:layout_height="50dip"></ImageView> <LinearLayout android:id="@+id/linearLayout1" android:orientation="vertical" android:layout_marginLeft="5px" android:layout_width="fill_parent" android:layout_height="match_parent"> <TextView android:layout_width="fill_parent" android:text="" android:layout_height="wrap_content" android:id="@+id/textViewTitle"></TextView> <TextView android:layout_width="fill_parent" android:text="" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_height="wrap_content" android:id="@+id/textViewLink"></TextView> </LinearLayout> </LinearLayout>
|
- Adapter 구현
Image 를 화면에 보여지기 위해서는 이미지 처리하는 작업이 추가로 들어가야 한다. Image 는 외부에 존재하는 파일이고, 외부에 존재하는 Image 의 link 정보만 가지고 있다. ImageView 에 link 로 연결되어 있는 이미지를 보여지게 하기 위해서는 별도의 Adapter(CustomAdapter) 를 생성하여 처리하여야 한다.
따라서 아래와 같이 ImageBaseAdapter 클래스를 생성하여 구현한다.
package com.coremodeling.naver.image;
import java.io.BufferedInputStream; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.util.List;
import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;
import com.coremodeling.naver.R;
public class ImageBaseAdapter extends BaseAdapter {
private List<Image> imageList;
private LayoutInflater layoutInflater;
public ImageBaseAdapter(Context context, List<Image> imageList) { this.imageList = imageList;
layoutInflater = (LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); }
public int getCount() { return imageList.size(); }
public Object getItem(int position) { return imageList.get(position); }
public long getItemId(int position) { return position; }
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) { view = layoutInflater.inflate(R.layout.image_row, null); } else { view = convertView; }
ImageView imageView = (ImageView)view.findViewById(R.id.imageView1); TextView textViewTitle = (TextView)view.findViewById(R.id.textViewTitle); TextView textViewLink = (TextView)view.findViewById(R.id.textViewLink);
String title = imageList.get(position).getTitle(); String link = imageList.get(position).getLink().toString(); String thumbnail = imageList.get(position).getThumbnail();
try {
URL url = new URL(thumbnail); URLConnection conn = url.openConnection(); conn.connect(); BufferedInputStream bis = new BufferedInputStream(conn.getInputStream()); Bitmap bm = BitmapFactory.decodeStream(bis); bis.close(); imageView.setImageBitmap(bm);
} catch (IOException e) { e.printStackTrace(); }
textViewTitle.setText(title); textViewLink.setText(link);
return view; }
}
|
- Activity 구현
Activity 를 아래와 같이 구현한다.
package com.coremodeling.naver;
import java.util.List;
import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText;
import com.coremodeling.naver.image.Image; import com.coremodeling.naver.image.ImageBaseAdapter; import com.coremodeling.naver.image.ImageFeedParser; import com.coremodeling.naver.image.SaxImageFeedParser;
public class NaverApiActivity extends ListActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.imagelist); }
public void onClickSearch(View view) { loadData(); }
private void loadData() {
String imageUrl = "http://openapi.naver.com/search?key=네이버검색키 입력" + "&target=image&start=1&display=20&query=";
EditText editText = (EditText)findViewById(R.id.editImageSearch);
String query = editText.getText().toString();
ImageFeedParser imageFeedParser = new SaxImageFeedParser(imageUrl + query);
List<Image> imageList = imageFeedParser.parse();
ImageBaseAdapter imageBaseAdapter = new ImageBaseAdapter(this, imageList);
setListAdapter(imageBaseAdapter);
} }
|
그리고 인터넷에 접근할 수 있는 권한을 사용하기 위해서, AndroidManifest.xml 파일에 INTERNET 을 use permission 으로 추가해야 한다.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.coremodeling.naver" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" /> <uses-permission android:name="android.permission.INTERNET"></uses-permission>
<application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NaverApiActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
</application> </manifest> |
실행
아래와 같이 실행한 결과를 확인할 수 있다.
- 추가 설정
ImageView 를 사용하여 ListView 에 목록으로 보여질 경우, Image 를 변환하는 작업을 수행해야 한다. Bitmap 도는 Drawable 로 Image 를 가져와서 변환해야만 목록에 이미지가 보여진다. 특히 외부에 이미지 정보가 링크로 연결되어 있는 경우에는 이러한 작업을 코딩을 통하여 수행한다.
하지만 이러한 작업을 좀 더 쉽게할 수 있는 방법이 WebView 를 사용하여 Image 처리를 하게 되면 좀더 편리하게 할 수 있다. Web 환경에서는 이미지의 link 정보만 있으면 브라우저가 해당 이미지를 가져와서 화면에 보여지도록 처리를 해준다. 이러한 원리로 ImageView 를 사용하는 대신에 webview 를 사용하여 이미지를 보이도록 처리할 수 있다.
/res/layout/image_row2.xml 파일을 생성하고 아래와 같이 구현한다.
<?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="match_parent"> <WebView android:layout_width="50dip" android:id="@+id/webView1" android:focusable="false" android:layout_height="50dip"></WebView> <LinearLayout android:id="@+id/linearLayout1" android:orientation="vertical" android:layout_marginLeft="5px" android:layout_width="wrap_content" android:layout_height="match_parent"> <TextView android:layout_width="fill_parent" android:text="" android:layout_height="wrap_content" android:id="@+id/textViewTitle"></TextView> <TextView android:layout_width="fill_parent" android:text="" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_height="wrap_content" android:id="@+id/textViewLink"></TextView> </LinearLayout> </LinearLayout> |
그리고 ImageBaseAdapter 를 아래와 같이 수정한다.
package com.coremodeling.naver.image;
import java.io.BufferedInputStream; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.util.List;
import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView;
import com.coremodeling.naver.R;
public class ImageBaseAdapter extends BaseAdapter {
private List<Image> imageList;
private LayoutInflater layoutInflater;
public ImageBaseAdapter(Context context, List<Image> imageList) { this.imageList = imageList;
layoutInflater = (LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); }
public int getCount() { return imageList.size(); }
public Object getItem(int position) { return imageList.get(position); }
public long getItemId(int position) { return position; }
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) { view = layoutInflater.inflate(R.layout.image_row2, null); } else { view = convertView; }
WebView webView = (WebView)view.findViewById(R.id.webView1); webView.setFocusable(false); TextView textViewTitle = (TextView)view.findViewById(R.id.textViewTitle); TextView textViewLink = (TextView)view.findViewById(R.id.textViewLink);
String title = imageList.get(position).getTitle(); String link = imageList.get(position).getLink().toString(); String thumbnail = imageList.get(position).getThumbnail();
webView.loadDataWithBaseURL(null, createImageHtml(thumbnail), "text/html", "utf-8", null);
textViewTitle.setText(title); textViewLink.setText(link); return view;
}
private String createImageHtml(String imageUrl) {
StringBuffer stringBuffer = new StringBuffer("<html>"); stringBuffer.append("<head></head>"); stringBuffer.append("<body>"); stringBuffer.append("<img width='50px' src='" + imageUrl + "' />"); stringBuffer.append("</body></html>"); return stringBuffer.toString(); }
} |
'Program > Android Java' 카테고리의 다른 글
[Android 2.3] DownloadManager 사용하기 (0) | 2011.09.21 |
---|---|
안드로이드에서 아이폰처럼 휠위젯 사용하기 (0) | 2011.09.21 |
안드로이드 앱 커스텀 글꼴 넣기 (0) | 2011.09.21 |
Text자동 완성: AutoCompleteTextView (0) | 2011.09.20 |
안드로이드 달력 (0) | 2011.08.17 |