공공데이터를 이용한 앱 만들어보기(3) - XML 파싱 및 RecyclerView 표현
2021. 11. 29. 20:37ㆍ앱개발
1. 클래스 생성(Station 정보)
더보기
public class Station {
public String bstopid;
public String bstopnm;
public String arsno;
public String gpsx;
public String gpsy;
public String stoptype;
public void clear(){
bstopid = "";
bstopnm = "";
arsno = "";
gpsx = "";
gpsy = "";
stoptype = "";
}
boolean checkRecvAllData(){
return bstopid.length() > 0
&& bstopnm.length() > 0
&& arsno.length() > 0
&& gpsx.length() > 0
&& gpsy.length() > 0
&& stoptype.length() > 0;
}
}
참고로 수신 받는 데이터의 형태는 아래와 같다
항목명(영문) | 항목명(국문) | 항목크기 | 항목구분 | 샘플데이터 | 항목설명 | |
resultCode | 결과코드 | 2 | 1 | 00 | 결과코드 | |
resultMsg | 결과메세지 | 50 | 1 | NORMAL SERVICE | 결과메세지 | |
numOfRows | 한 페이지 결과 수 | 4 | 1 | 10 | 한 페이지당 표출 데이터 수 | |
pageNo | 페이지 수 | 4 | 1 | 1 | 페이지 수 | |
totalCount | 데이터 총 개수 | 4 | 1 | 1 | 데이터 총 개수 | |
items | 목록 | 0..n | 목록 | |||
arsno | 정류소번호 | 5 | 1 | 13708 | 정류소 번호 | |
bstopid | 정류소아이디 | 9 | 1 | 505780000 | 정류소 아이디 | |
bstopnm | 정류소명 | 50 | 1 | 부산시청 | 정류소 이름 | |
gpsx | GPS X좌표 | 28 | 1 | 129.07691415917 | GPS X좌표 | |
gpsy | GPS Y좌표 | 28 | 1 | 35.179937855685 | GPS Y좌표 | |
stoptype | 정류장구분 | 4 | 1 | 일반 | 정류장구분 |
2. 수신 받는 루틴은 다음과 같고 ArrayList에 담을 수 있다
더보기
String curr_tag = "";
ArrayList<Station> arrStation = new ArrayList<>();
Station station = new Station();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader(response) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
//System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
//시작하는 tag 기억
curr_tag = xpp.getName();
if(xpp.getName().equals("item")){
station.clear();
}
} else if(eventType == XmlPullParser.END_TAG) {
//item 태그 종료시 추가
if(xpp.getName().equals("item")){
if(station.checkRecvAllData())
arrStation.add(station);
}
curr_tag = "";
} else if(eventType == XmlPullParser.TEXT) {
//태그 종류별로 기록
switch(curr_tag)
{
case "bstopid": station.bstopid = xpp.getText(); break;
case "bstopnm": station.bstopnm = xpp.getText(); break;
case "arsno": station.arsno = xpp.getText(); break;
case "gpsx": station.gpsx = xpp.getText(); break;
case "gpsy": station.gpsy = xpp.getText(); break;
case "stoptype":station.stoptype = xpp.getText(); break;
}
}
eventType = xpp.next();
}
} catch (Exception e) {
e.printStackTrace();
}
4. 그러면 RecyclerView에 표현하기 위해서 Adapter를 만들어야 한다
1) Adapter를 만들기 위해서 그전에 ViewHolder를 inner class로 만든다
더보기
public class StationAdapter{
//버스 정류장 id와 이름을 표현할 text view를 찾는다
//화면에 표시하기 위한 메소드 정의( setItem )
static class ViewHolder extends RecyclerView.ViewHolder {
TextView bus_id;
TextView bus_nm;
public ViewHolder(@NonNull View itemView) {
super(itemView);
bus_id = itemView.findViewById(R.id.bus_id);
bus_nm = itemView.findViewById(R.id.bus_name);
}
public void setItem(Station station){
bus_id.setText(station.bstopid);
bus_nm.setText(station.bstopnm);
}
}
}
2) 그리고 나서 Adapter를 완성한다
OnCreateViewHolder는 메모리에 생성할 때 쓰여지는 메소드이고, onBindViewHolder는 이미 생성된 ViewHolder를 재사용할 때 사용되는 메소드이다
더보기
public class StationAdapter extends RecyclerView.Adapter<StationAdapter.ViewHolder>{
ArrayList<Station> items = new ArrayList<Station>();
public void setItems(ArrayList<Station> items){this.items = items;}
public void clearItems(){ items.clear(); }
public void addItem(Station station){ items.add(station); }
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.station_item, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Station item =items.get(position);
holder.setItem(item);
}
@Override
public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView bus_id;
TextView bus_nm;
public ViewHolder(@NonNull View itemView) {
super(itemView);
bus_id = itemView.findViewById(R.id.bus_id);
bus_nm = itemView.findViewById(R.id.bus_name);
}
public void setItem(Station station){
bus_id.setText(station.bstopid);
bus_nm.setText(station.bstopnm);
}
}
}
3. 그럼 MainActivity에서 다음과 같은 작업을 진행한다
아래와 같은 절차를 거친다
더보기
//1. adapter 생성
adapter = new StationAdapter();
//2. RecyclerView 변수 할당
rvStation = findViewById(R.id.rvStation);
//3. RecyclerView에 layoutManager 할당
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rvStation.setLayoutManager(layoutManager);
//4. RecyclerView에 adapter 할당
rvStation.setAdapter(adapter);
//5. item 태그가 시작되면 Station 새로운 메모리 할당
//같은 객체를 계속 쓰면 리스트에 모두 같은 객체를 참조하므로 같은 출력물이 나온다
//6. xml 수신 받기 시작할 때 adapter의 리스트 clear
adapter.clearItems();
//7. 객체 하나가 완성되면 adapter에 add
adapter.addItem(station);
//8. 수신이 완료되면 adapter에 notify
adapter.notifyDataSetChanged();
4. 완성 코드는 다음과 같다 (개발자 코드는 숨겼음)
더보기
package com.hdongle.busanbusstationinfotest;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
public class MainActivity extends AppCompatActivity {
RecyclerView rvStation;
static RequestQueue requestQueue;
String TAG = "STATION LIST";
StationAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
adapter = new StationAdapter();
rvStation = findViewById(R.id.rvStation);
rvStation.setLayoutManager(layoutManager);
rvStation.setAdapter(adapter);
requestQueue = Volley.newRequestQueue(getApplicationContext());
Button btn = findViewById(R.id.btnRequest);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StringRequest request = new StringRequest(
Request.Method.GET,
"http://apis.data.go.kr/6260000/BusanBIMS/busStopList?serviceKey=__________&pageNo=1&numOfRows=100",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//Log.d(TAG, "onResponse : " + response);
String curr_tag = "";
//ArrayList<Station> arrStation = new ArrayList<>();
Station station = new Station();
adapter.clearItems();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader(response) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
//System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
//시작하는 tag 기억
curr_tag = xpp.getName();
if(xpp.getName().equals("item")){
station = new Station();
}
} else if(eventType == XmlPullParser.END_TAG) {
//item 태그 종료시 추가
if(xpp.getName().equals("item")){
if(station.checkRecvAllData()){
adapter.addItem(station);
}
//arrStation.add(station);
}
curr_tag = "";
} else if(eventType == XmlPullParser.TEXT) {
//태그 종류별로 기록
switch(curr_tag)
{
case "bstopid": station.bstopid = xpp.getText(); break;
case "bstopnm": station.bstopnm = xpp.getText(); break;
case "arsno": station.arsno = xpp.getText(); break;
case "gpsx": station.gpsx = xpp.getText(); break;
case "gpsy": station.gpsy = xpp.getText(); break;
case "stoptype":station.stoptype = xpp.getText(); break;
}
}
eventType = xpp.next();
}
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println("Count : " + arrStation.size());
adapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "수신완료", Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "onErrorResponse : " + error.toString());
}
}
){
//요청 파라미터를 처리하는 메소드
@Nullable
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//요청 객체가 하나 만들어짐
Map<String, String> params = new HashMap<String, String>();
//요청 큐에 넣어주면 된다
return super.getParams();
}
};
request.setShouldCache(false);
requestQueue.add(request);
}
});
//XmlParserCreator parserCreator;
/*
XmlParserCreator parserCreator = new XmlParserCreator() {
@Override
public XmlPullParser createParser() {
try {
return XmlPullParserFactory.newInstance().newPullParser();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
GsonXml gsonXml = new GsonXmlBuilder()
.setXmlParserCreator(parserCreator)
.create();
*/
}
}
결과는 다음과 같다.
허접하긴 한데, 뭔가 나왔다는데 의의를 둔다..ㅎㅎㅎ
'앱개발' 카테고리의 다른 글
C# 소켓 연결 여부 체크 (0) | 2022.05.27 |
---|---|
[안드로이드] 탭 레이아웃 - 작성중 (0) | 2021.12.07 |
공공데이터를 이용한 앱 만들어보기(2) - 데이터 요청 및 수신 (0) | 2021.11.27 |
[안드로이드] 권한 관련 코드들 (0) | 2021.11.23 |
공공데이터를 이용한 앱 만들어보기(1) - 공공데이터 신청 및 화면 구성 (0) | 2021.11.22 |