안녕하세요.
안드로이드에서 네이버 Image 를 읽어오는 강좌를 만들었습니다.
아래 링크를 확인해보시기 바랍니다.
http://www.coremodeling.com/android/tutorial/NaverImageFeed/NaverImageFeed.html
안드로이드에서 네이버 Image API 사용(Naver Image Feed) 안드로이드 핸드폰에서 Naver Image 를 읽어와서 어플리케이션에 보여주는 기능을 구현한다. 이전 자료에서는 News 목록을 보여지도록 처리했지만, 이번에는 이미지를 함께 보여주는 기능을 구현해본다. Image 처리는 News 보다 좀 더 복잡한 구조를 가지고 있다. 우선 Project 를 먼저 생성하여, Application 을 개발하기 위한 초기 작업을 수행한다. 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); } } 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(); } 서버와 접속하여, 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); } } } SAXParsing 을 하기 위한 DefaultHandler 클래스를 상속받는 RssHandler 클래스를 정의한다. 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(); } } } 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> 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 를 아래와 같이 구현한다. 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 를 가져와서 변환해야만 목록에 이미지가 보여진다. 특히 외부에 이미지 정보가 링크로 연결되어 있는 경우에는 이러한 작업을 코딩을 통하여 수행한다. /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(); } }
네이버의 Open API 를 사용하여 뉴스, 이미지, 블로그 등등을 조회하기 위해서는 우선 Naver 에 회원가입이 되어있어야 하고, 네이버에 로그인을 해야 한다.
Open API 는 누구든지 key 를 발급 받으면 사용할 수 있으며, 일일동안에 접속하는 데이터량의 제한이 있다.
아래 이미지는 네이버 개발자 센터 홈페이지이다.
위 이미지에서 검색 API 메뉴를 클릭하면 검색 API 목록이 나온다. 검색 API 목록 중에서 이번 예제에서는 News 와 Image 목록을 안드로이드에서 조회하는 어플리케이션을 작성하려고 한다.
우선 검색을 하기 위해서는 네이버 개발사 사이트에서 키를 발급받아야 한다. 키를 발급받는 링크는 왼쪽 메뉴에 “키 등록/관리” 메뉴를 클릭한다.
검색 API 에 대한 key 와 지도 API key 두 가지 형태의 key 를 발급받을 수 있으며, 지도 API key 를 사용하고자 한다면, “키 추가” 버튼을 클릭하여 키를 추가한다.
아래 화면은 필자가 사용하는 key 를 발급 받은 내역이다.
이 XML 문서를 Parsing 하여 Andorid Application 에서 목록(ListView)으로 보여주려고 한다.
Android 에서는 XML Parsing 하는데 있어, 크게 3 가지 형태를 사용하고 있다.
3 가지 예제에 대해서는 앞에서 작성한 News Feed 예제를 참고하길 바란다.
https://docs.google.com/document/pub?id=1aHv43x9oYxL8dUCAtJ1ieYScyDPoQY9ErhyhSWo_Qz8
Eclipse 를 실행하고, File -> New -> Android Project 메뉴를 선택한다.
(기존에 News Feed 프로젝트를 생성하였으며, 그대로 패키지만 추가하여 사용하면 된다.)
하나의 Image 를 저장하는 Image 클래스를 생성한다. Open API 를 사용하여, XML 데이터를 Parsing 하게 되면, 각각의 Image 의 정보를 저장하기 위한 객체가 필요하다. 하나의 Image 를 저장하기 위한 객체를 만들기 위해서 Image 라는 이름의 클래스를 생성한다. 이 클래스에는 Naver 에서 Image 의 정보를 제공해주는 각 항목을 저장하기 위한 멤버변수가 존재하게 된다.
위의 XML 데이터에서 확인했듯이, 하나의 Image 에 대한 여러가지 내용을 저장하기 위한 멤버변수를 정의한다. Title, Link, Thumbnail, Sizeheight, Sizewidth 등이 기본적으로 저장된다.
따라서 아래와 같이 Image 클래스를 생성한다.ImageFeedParser Interface
BaseImageFeedParser Abstract Class
ImageRssHandler 클래스
XML 데이터를 받고 나서, XML 데이터를 처음부터 끝까지 데이터를 읽으면서 Parsing 하는 작업을 수행한다.SaxImageFeedParser 클래스
Image 를 화면에 보여지기 위해서는 이미지 처리하는 작업이 추가로 들어가야 한다. Image 는 외부에 존재하는 파일이고, 외부에 존재하는 Image 의 link 정보만 가지고 있다. ImageView 에 link 로 연결되어 있는 이미지를 보여지게 하기 위해서는 별도의 Adapter(CustomAdapter) 를 생성하여 처리하여야 한다.
따라서 아래와 같이 ImageBaseAdapter 클래스를 생성하여 구현한다.실행
하지만 이러한 작업을 좀 더 쉽게할 수 있는 방법이 WebView 를 사용하여 Image 처리를 하게 되면 좀더 편리하게 할 수 있다. Web 환경에서는 이미지의 link 정보만 있으면 브라우저가 해당 이미지를 가져와서 화면에 보여지도록 처리를 해준다. 이러한 원리로 ImageView 를 사용하는 대신에 webview 를 사용하여 이미지를 보이도록 처리할 수 있다.
궁금한게 있는데요 이걸 응요해서 맛집 정보도 할수 잇겠죠??? 검색기능을 응용 해야 할듯싶은데..맞나요?^^