안녕하세요.

안드로이드에서 네이버 Image 를 읽어오는 강좌를 만들었습니다.

아래 링크를 확인해보시기 바랍니다.


http://www.coremodeling.com/android/tutorial/NaverImageFeed/NaverImageFeed.html


안드로이드에서 네이버 Image API 사용(Naver Image Feed)


  1. 작성자 : 고덕한(deokhan.koh@gmail.com)
  2. 소속 : 코아모델링(www.coremodeling.com)
  3. 작성일자 : 2011년 9월 15일

안드로이드 핸드폰에서 Naver Image 를 읽어와서 어플리케이션에 보여주는 기능을 구현한다.

이전 자료에서는 News 목록을 보여지도록 처리했지만, 이번에는 이미지를 함께 보여주는 기능을 구현해본다.

Image 처리는 News 보다 좀 더 복잡한 구조를 가지고 있다.

  1. 네이버 계정으로 Open API 접속
    네이버의 Open API 를 사용하여 뉴스, 이미지, 블로그 등등을 조회하기 위해서는 우선 Naver 에 회원가입이 되어있어야 하고, 네이버에 로그인을 해야 한다.
    Open API 는 누구든지 key 를 발급 받으면 사용할 수 있으며, 일일동안에 접속하는 데이터량의 제한이 있다.
    아래 이미지는 네이버 개발자 센터 홈페이지이다.

  2. Open API 확인
    위 이미지에서 검색 API 메뉴를 클릭하면 검색 API 목록이 나온다. 검색 API 목록 중에서 이번 예제에서는 News 와 Image 목록을 안드로이드에서 조회하는 어플리케이션을 작성하려고 한다.
    우선 검색을 하기 위해서는 네이버 개발사 사이트에서 키를 발급받아야 한다. 키를 발급받는 링크는 왼쪽 메뉴에 “키 등록/관리” 메뉴를 클릭한다.
    검색 API 에 대한 key 와 지도 API key 두 가지 형태의 key 를 발급받을 수 있으며, 지도 API key 를 사용하고자 한다면, “키 추가” 버튼을 클릭하여 키를 추가한다.
    아래 화면은 필자가 사용하는 key 를 발급 받은 내역이다.




    이 XML 문서를 Parsing 하여 Andorid Application 에서 목록(ListView)으로 보여주려고 한다.

  1. XML Parsing
    Android 에서는 XML Parsing 하는데 있어, 크게 3 가지 형태를 사용하고 있다.
    3 가지 예제에 대해서는 앞에서 작성한 News Feed 예제를 참고하길 바란다.
    https://docs.google.com/document/pub?id=1aHv43x9oYxL8dUCAtJ1ieYScyDPoQY9ErhyhSWo_Qz8
  2. Project 생성

우선 Project 를 먼저 생성하여, Application 을 개발하기 위한 초기 작업을 수행한다.
Eclipse 를 실행하고, File -> New -> Android Project 메뉴를 선택한다.
(기존에 News Feed 프로젝트를 생성하였으며, 그대로 패키지만 추가하여 사용하면 된다.)

  1. Project name : NaverApi
  2. Build Target : Android 2.3.3
  3. Package : com.coremodeling.naver

  1. 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);

        }

        

}

  1. 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();

}

  1. 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);

       }

   }

}

  1. 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();

                }

        }

}

  1. 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();

    }

}

  1. 화면 디자인

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>

  1. 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;

        }

}

  1. 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>

  1. 실행

아래와 같이 실행한 결과를 확인할 수 있다.

  1. 추가 설정

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();

        }

}