개발하는 두더지

[Android/안드로이드] 다음 쇼핑 api 를 이용한 상품 검색 프로그램 ( 다음 API, JSON, ListView 활용 ) 본문

Java,Android

[Android/안드로이드] 다음 쇼핑 api 를 이용한 상품 검색 프로그램 ( 다음 API, JSON, ListView 활용 )

덜지 2016. 7. 22. 03:05

개요

다음에서 제공하는 쇼핑 api를 이용하여 URL에서 결과를 JSON으로 받아와서 JSON파서를 이용해

ArrayList에 저장하여 화면에 뷰를 출력

모바일 DB SQLite3에 저장하는 기능 추가 구현


결과물

1. 다음 API 사용 방법 및 활용

2. JSON , 파싱 이해 및 활용

3. CUSTOM LISTVIEW 이해 및 활용

4. 프로젝트 코드



네이버 블로그에서 가져오면서 사진이 전부..... 사라져버렸다.


다음 API 키를 발급받는다. 

https://apis.daum.net/shopping/search?apikey=본인의KEY&q=상품명&result=20&pageno=1&output=json&sort=pop
다음과 같이 발급받은 KEY를 이용하여 쿼리를 만들 수 있다.

Root -> array -> channel -> item

Root와 channel 를 JSONObject 로 얻어온 뒤 item Array객체를 얻어와서 데이터를 파싱하면된다.
자세한건 다음 목차에서 살펴보겠다.
2. JSON , 파싱 이해 및 활용
org.json은 안드로이드 스튜디오에서는 내장되어 있다.
JSONObject

A modifiable set of name/value mappings. Names are unique, non-null strings. Values may be any mix of JSONObjectsJSONArrays, Strings, Booleans, Integers, Longs, Doubles or NULL. Values may not be nullNaNs,infinities, or of any type not listed here. 
// 주의할 점은 JAVA의 null은 데이터를 지우는데 쓰는것이고, JSONObject.NULL은 null 값을 넣는다는 것이다. 
( 전자도 결국 null 값을 넣는게 아닌가? )
Warning: this class represents null in two incompatible ways: the standard Java null reference, and the sentinel value NULL. In particular, calling put(name, null) removes the named entry from the object but put(name, JSONObject.NULL) stores an entry whose value is JSONObject.NULL.
public JSONObject (String json)
Added in API level 1

Creates a new JSONObject with name/value mappings from the JSON string.

Parameters
jsona JSON-encoded string containing an object.
Throws
JSONExceptionif the parse fails or doesn't yield a JSONObject.


public JSONObject getJSONObject (String name)
Added in API level 1

Returns the value mapped by name if it exists and is a JSONObject, or throws otherwise.

Throws
JSONExceptionif the mapping doesn't exist or is not a JSONObject.

 


public JSONArray getJSONArray (String name)

Added in API level 1

Returns the value mapped by name if it exists and is a JSONArray, or throws otherwise.

Throws
JSONExceptionif the mapping doesn't exist or is not a JSONArray.


 

JSONArray

A dense indexed sequence of values. Values may be any mix of JSONObjects, other JSONArrays, Strings, Booleans, Integers, Longs, Doubles, null or NULL. Values may not be NaNsinfinities, or of any type not listed here.

JSONArray has the same type coercion behavior and optional/mandatory accessors as JSONObject. See that class' documentation for details.


 Root -> array -> channel -> item 

item의 객체를 얻어오려면 다음 코드와 같다


// result는 쿼리 결과이고, 그 값(String) 으로 JSON 객체를 만든다.

// 트리구조라 생각해보면 Root를 시작으로 한 트리의 객체를 가지게 된다고 이해하면 된다.


JSONObject root = new JSONObject(result);
// 우리가 원하는 item 은 Root 아래의 channel 트리에 있다.
// 루트에서 channel 이라는 이름을 가진 객체를 가지고와서 channel을 시작으로 하는 JSON 객체를 만든다.

JSONObject channelObject = root.getJSONObject("channel");
// channel 에서 item 은 리스트로 구성이 되어있으므로 JSONArray 객체로 받아온다.
JSONArray itemArray = channelObject.getJSONArray("item");
// ltem 의 갯수만큼 돌면서 ltem 안에 있는 Object를 가져와서 그 안에 있는 데이터를 하나씩 파싱한다. 
StringBuilder sb2 = new StringBuilder();
for (int i=0; i< itemArray.length(); i++){
sb2.setLength(0);
JSONObject jsonItem = itemArray.getJSONObject(i);

String title = "Title : " + jsonItem.getString("title");
sb2.append(title + "\n");
String maker = "Maker : " + jsonItem.getString("maker");
sb2.append(maker + "\n");
String price = "Price : " + jsonItem.getInt("price_min") + " ~ " + jsonItem.getInt("price_max");
sb2.append(price + "\n");

Drawable image = loadImage(jsonItem.getString("image_url"));

listViewAdapter.addItem(false, image, sb2.toString());
}
상품명, 브랜드, 최저가격, 최대가격, 상품이미지를 가져와서 저장한 뒤 그 값을 ListAdapter에 add해주면 결과가 출력된다.
3. Custom ListView  구현 및 활용
private class ListViewAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<ListData> mItem = new ArrayList<ListData>();

public ListViewAdapter(Context context) {
super();
mContext = context;
}

public void allRemove(){
mItem.clear();
}

@Override
public int getCount() {
return mItem.size();
}

@Override
public Object getItem(int position) {
return mItem.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

public void addItem(boolean check, Drawable image, String text) {
ListData addInfo = null;
addInfo = new ListData();
addInfo.mCheck = check;
addInfo.mImageView = image;
addInfo.mTextView = text;

mItem.add(addInfo);
}

public void remove(int position) {
mItem.remove(position);
dataChange();
}

public void dataChange() {
listViewAdapter.notifyDataSetChanged();
}

@Override
//position 행의 index
//convertView 행의 전체를 나타내는 뷰
// parent 부모 뷰 즉 리스트뷰
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();

// 시스템으로부터 레이아웃 리소스를 인플레이트하는 객체를 얻어오기
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 어댑터 객체 만들 떼 보여줄 행의 레이아웃을 정하듯이 inflate로 내가 만든 xml을 행의 레이아웃으로 지정
convertView = inflater.inflate(R.layout.listview_item, null);

// 뷰홀더는 내가 만든 행의 레이아웃의 속성들로 구성
viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
viewHolder.textView = (TextView) convertView.findViewById(R.id.textView);

// 커스텀 리스트 아이템의 UI 객체들을 미리 찾아서 Tag에 넣어두고 미리 찾아둔 tag를 꺼내 쓰는 방식
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

// 뷰홀더 안의 실제 값
ListData mData = mItem.get(position);

viewHolder.checkBox.setChecked(mData.mCheck);

if (mData.mImageView != null) {
viewHolder.imageView.setVisibility(View.VISIBLE);
viewHolder.imageView.setImageDrawable(mData.mImageView);
} else {
viewHolder.imageView.setVisibility(View.GONE);
}

viewHolder.textView.setText(mData.mTextView);

return convertView;
}
}
BaseAdapter 를 상속받아 만든 리스트 어댑터로 리스트 뷰에서 내가 원하는 대로 리스트 행을 구현할 수 있다.</pre>
오버라이드된 getView 메소드는 리스트에 변화가 있을 때 마다 리스트 갯수 또는 그 이상 호출된다.
리소스와 속도 향상을 위해 ViewHolder라는 것을 사용한다.

/**
* Created by KJH on 2015-11-10.
* 전체가 public 으로 구현된 간단한 클래스로
* 멤버변수에 접근하는 비용을 줄이는 ViewHolder 패턴을 이용
* Row내의 요소 위젯들을 직접적으로 가지고 있으므로 바로바로 값을 변경할 수 있다.
* 실제로 많은 Row를 가져도 빠르게 동작한다.
*/
public class ViewHolder {
public CheckBox checkBox;
public ImageView imageView;
public TextView textView;
}
 
커스텀 어댑터를 리스트 뷰에 set 시켜주면 처음에 행을 나타내는 뷰는 구현이 되어있지 않다.
그래서 convertView 가 null 일 때, 리스트의 행 레이아웃을 구현해줘야 한다.
초반부에 말했듯이 getView는 리스트 갯 수 만큼 호출된다. 그런데 호출될 때 마다 새로운 레이아웃 객체를 만들어서
세팅해준다면 상당한 비용이 발생하게 된다. 
그래서 뷰 홀더를 만들고 convertView 에 setTag(뷰 홀더); 로 저장해주고 
getView가 호출될 때 마다 getTag()로 뷰 홀더를 가져오는 방식이다.


Comments