이번 포스팅에서는 커스텀 리스트뷰를 이용하여, 네이버 검색 API를 통해 얻어온 결과값을 띄워보는 예제를 포스팅하고자 한다.
리스트뷰와, 커스텀 리스트뷰의 차이점을 설명하자면 말그대로 custom하게 꾸밀 수 있다는 점이다.
원하는대로 이미지를 넣거나, 다양한 UI들을 구현할 수 있다는 장점이 있다.
기본적으로 custom listview를 사용하기 위해서는 다음과 같이 5개의 파일이 필요하다.
Java
1. MainActivity.java: Custom ListView가 구현될 Activity (사용자가 보는 화면에 해당하는 java)
2. Adapter.java: listView Layout을 inflate. (Activty와 Data 사이 중간역할. 리스트뷰 xml을 받아서 메인화면에 띄워줌)
3. Data.java: listView에 들어갈 변수들을 담는 그릇. (Getter, Setter)
XML
1. activity.xml: 사용자가 보는 화면
2. listView.xml: 리스트뷰를 정의하는 화면
네이버나 구글에 자료가 정~말 많은데, 대체로 원리적인 내용들이라 이해하기가 좀 난해했다. 간단하게 생각해보면, 다음 그림처럼 생각해볼 수 있다.
더 복잡해보일 수도 있지만.. 무튼 MainActivity 안에, 커스텀리스트뷰라는 새로운 xml을 담기 위해 중간에 Adaptor을 사용했다고 보면 된다. 어댑터를 사용하지 않으면, 하나의 자바 클래스에 2개의 xml파일을 어떻게 넣을 것인가?
대충 이정도로 이해하고, 바로 코드를 살펴보도록 하자.
MainActivity.Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import static android.content.ContentValues.TAG; public class MainActivity extends AppCompatActivity { StringBuilder searchResult; BufferedReader br; String[] title, link, description, bloggername, postdate; SNSViewAdaptor mMyAdapter; private ListView mListView; int itemCount; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitymain); searchNaver("검색어를 여기에 입력"); mListView = (ListView) findViewById(R.id.listViewSNS); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String blog_url = mMyAdapter.getItem(position).getBloglink(); Intent intent = new Intent(getApplicationContext(), InternetWebView.class); intent.putExtra("blog_url", blog_url); startActivity(intent); } }); } public void searchNaver(final String searchObject) { final String clientId = "";//애플리케이션 클라이언트 아이디값"; final String clientSecret = "";//애플리케이션 클라이언트 시크릿값"; final int display = 5; // 보여지는 검색결과의 수 // 네트워크 연결은 Thread 생성 필요 new Thread() { @Override public void run() { try { String text = URLEncoder.encode(searchObject, "UTF-8"); String apiURL = "https://openapi.naver.com/v1/search/blog?query=" + text + "&display=" + display + "&"; // json 결과 URL url = new URL(apiURL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setRequestProperty("X-Naver-Client-Id", clientId); con.setRequestProperty("X-Naver-Client-Secret", clientSecret); con.connect(); int responseCode = con.getResponseCode(); if(responseCode==200) { // 정상 호출 br = new BufferedReader(new InputStreamReader(con.getInputStream())); } else { // 에러 발생 br = new BufferedReader(new InputStreamReader(con.getErrorStream())); } searchResult = new StringBuilder(); String inputLine; while ((inputLine = br.readLine()) != null) { searchResult.append(inputLine + "\n"); } br.close(); con.disconnect(); String data = searchResult.toString(); String[] array = data.split("\""); title = new String[display]; link = new String[display]; description = new String[display]; bloggername = new String[display]; postdate = new String[display]; itemCount = 0; for (int i = 0; i < array.length; i++) { if (array[i].equals("title")) title[itemCount] = array[i + 2]; if (array[i].equals("link")) link[itemCount] = array[i + 2]; if (array[i].equals("description")) description[itemCount] = array[i + 2]; if (array[i].equals("bloggername")) bloggername[itemCount] = array[i + 2]; if (array[i].equals("postdate")) { postdate[itemCount] = array[i + 2]; itemCount++; } } Log.d(TAG, "title잘나오니: " + title[0] + title[1] + title[2]); // 결과를 성공적으로 불러오면, UiThread에서 listView에 데이터를 추가 runOnUiThread(new Runnable() { @Override public void run() { listViewDataAdd(); } }); } catch (Exception e) { Log.d(TAG, "error : " + e); } } }.start(); } public void listViewDataAdd() { mMyAdapter = new SNSViewAdaptor(); for (int i = 0; i < itemCount; i++) { mMyAdapter.addItem(Html.fromHtml(title[i]).toString(), Html.fromHtml(description[i]).toString(), Html.fromHtml(bloggername[i]).toString(), Html.fromHtml(postdate[i]).toString(), Html.fromHtml(link[i]).toString()); } // set adapter on listView mListView.setAdapter(mMyAdapter); } } | cs |
activitymain.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <LinearLayout android:id="@+id/listViewLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="vertical"> <ListView android:id="@+id/listViewSNS" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentStart="true"> </ListView> </LinearLayout> | cs |
SNSViewAdaptor.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import java.util.ArrayList; public class SNSViewAdaptor extends BaseAdapter{ /* 데이터 그릇들의 집합을 정의 */ private ArrayList<SNSViewItem> mItems = new ArrayList<>(); @Override public int getCount() { return mItems.size(); } @Override public SNSViewItem getItem(int position) { return mItems.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { Context context = parent.getContext(); // 커스텀 리스트뷰의 xml을 inflate if (convertView == null) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.listview_custom, parent, false); } /* 커스텀 리스트뷰 xml에 있는 속성값들을 정의 */ TextView blog_title = (TextView) convertView.findViewById(R.id.blog_title); TextView blog_description = (TextView) convertView.findViewById(R.id.blog_description); TextView blogger_name = (TextView) convertView.findViewById(R.id.blogger_name); TextView post_date = (TextView) convertView.findViewById(R.id.post_date); /* 데이터를 담는 그릇 정의 */ SNSViewItem snsViewItem = getItem(position); /* 해당 그릇에 담긴 정보들을 커스텀 리스트뷰 xml의 각 TextView에 뿌려줌 */ blog_title.setText(snsViewItem.getTitle()); blog_description.setText(snsViewItem.getDescription()); blogger_name.setText(snsViewItem.getBloggername()); post_date.setText(snsViewItem.getPostdate()); return convertView; } /* 네이버 블로그 검색 중, 제목, 내용, 블로거이름, 포스팅 일자, 포스트 링크를 그릇에 담음 */ public void addItem(String title, String description, String bloggername, String postdate, String link) { SNSViewItem mItem = new SNSViewItem(); mItem.setTitle(title); mItem.setDescription(description); mItem.setBloggername(bloggername); mItem.setPostdate(postdate); mItem.setBloglink(link); /* 데이터그릇 mItem에 담음 */ mItems.add(mItem); } } | cs |
listview_custom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:id="@+id/blog_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="1" android:text="blog_title" android:textColor="#000000" android:textSize="15dp" android:textStyle="bold" /> <TextView android:id="@+id/blog_description" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="3" android:text="blog description" android:textSize="15dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal"> <TextView android:id="@+id/blogger_name" android:layout_width="wrap_content" android:layout_height="match_parent" android:ellipsize="end" android:maxLines="1" android:text="blogger name" android:textColor="#000000" android:textSize="12dp" /> <TextView android:id="@+id/textView6" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:text="-" /> <TextView android:id="@+id/post_date" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:ellipsize="end" android:maxLines="1" android:text="post date" android:textSize="12dp" /> </LinearLayout> </LinearLayout> </LinearLayout> | cs |
SNSViewItem.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class SNSViewItem { private String title; private String description; private String bloggername; private String postdate; private String bloglink; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getBloggername() { return bloggername; } public void setBloggername(String bloggername) { this.bloggername = bloggername; } public String getPostdate() { return postdate; } public void setPostdate(String postdate) { this.postdate = postdate; } public String getBloglink() { return bloglink; } public void setBloglink(String bloglink) { this.bloglink = bloglink; } } | cs |
우선 위 5가지 파일이 처음에 설명한, 커스텀 리스트뷰를 구성하기 위한 필수적인 부분이다. 네이버 검색 API를 사용하는 방법이 조금 들어있는데, 이 부분은 지난 포스팅을 참고하시길.
설명이 안된 부분들이 있는데, 크게 다음 2가지부분이다.
1. 네이버 검색 결과를 위 포스팅에 의해 가져오게 되면, 태그가 같이 String값에 저장되는 것을 볼 수 있다. 네이버 검색의 경우, 예를들어 검색어 "티스토리"로 검색하게 되면 검색어와 일치하는 부분이 굵은 글씨로 표시가 되는데, 굵은 글씨를 의미하는 <b> </b> 태그가 같이 나온다는 소리이다.
이를 위해MainActivity에서 Html.fromHtml(description[i]).toString()라는 코드를 삽입하여, HTML의 특성을 다 지운 후, 순수한 텍스트값만 String으로 저장했다.
2. 커스텀 리스트뷰의 특정 아이템을 클릭했을 때, OnclickListener을 사용하여 해당 블로그 웹페이지를 보여주고자 했다. MainActivity에서 mListView.setOnItemClickListener코딩 부분을 참조하면 된다. 보면 블로그 포스팅의 link, 즉 웹 URL 정보를 intent로 넘겨주는 걸 볼 수 있는데, 이는 WebView만 띄워놓은 다른 activity 파일에 URL을 전송해주기 위함이다. WebView XML은 단순해서 생략하기로 하고, WebView에 해당하는 Activity 클래스는 다음과 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; public class InternetWebView extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.internet_webview); WebView webView = (WebView) findViewById(R.id.webView); Intent intent = getIntent(); String blog_url = intent.getExtras().getString("blog_url"); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl(blog_url); webView.setWebChromeClient(new WebChromeClient()); webView.setWebViewClient(new WebViewClient()); } } | cs |
크게 어려운 부분은 없고, WebView id값을 찾아서 저장한 후, MainActivity에서 받은 인텐트값인 URL 정보를 받는다. 그 후, webView에 loadUrl로 받아주기만 하면 된다. (그 뒤에 나오는 setWebChromeClient 등은 인터넷 창을 여는 방식에 해당하는 부분으로, 일단 동일하게 쓰면 된다)
이렇게 네이버 블로그 검색 API를, 커스텀 리스트뷰에 띄우고, 해당 아이템 클릭스 온클릭리스너까지 적용하는 방법에 대해서 알아보았다.
'Developer > Android, Java' 카테고리의 다른 글
[안드로이드] Retrofit 기본 예제 (서버 통신) (260) | 2019.03.21 |
---|---|
[안드로이드] EditText 키보드 숨기는 기능 (자동으로 키보드 뜨는 것 방지) (258) | 2019.03.19 |
[안드로이드] ConstraintLayout, Guideline을 이용하여 삽입 개체 크기 비율 조절 (271) | 2019.03.19 |
[안드로이드] 지하철 역 클릭 시 이벤트 구현하기 1편 (258) | 2019.01.11 |
[안드로이드] 네이버 검색 API 사용 예제 (258) | 2019.01.07 |
댓글