공공데이터를 이용한 앱 만들어보기(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();
 */
    }


}

 

 

결과는 다음과 같다. 

허접하긴 한데, 뭔가 나왔다는데 의의를 둔다..ㅎㅎㅎ