[안드로이드] 공공데이터 기차역 정보 조회 서비스 API 사용 [3]











이번에 포스팅 할 내용은,


[1]에서 열차정보와 코드를 구성했던 .txt 파일을


Android에 올리고, ListView로 구성 및 'ㄱㄴㄷ'순으로 정렬 및 EditText이용한 초성검색 입니다.





기차역 API 조회 결과


























[2]에서 구성하였던 화면,




[출발역 설정] 버튼과 [도착역 설정]버튼이 눌러졌을시 등장하는 Activity입니다



이를 구성하기 위해,



Manifest 수정을 해 둘 필요성이 있습니다.




<activity android:name=".StationListActivity" />


화면 전환 할 Activity 클래스를 추가해 주셔야 합니다.


함수 별로 나눠서 설명하겠습니다.


이것을 테스트 하려면, 아마 모바일 기기를 사용하셔야 될 것입니다.


AVD로는 안해봣지만, 안 될것 같긴 한데 잘은 모르겠네요 ㅎㅎ...



일단 준비사항으로는, 프로젝트에 들어가셔서 app -> src -> assets 폴더안에 InfoTrainStation.txt 파일과 InfoTrainStationCode.txt 를 만들어 주셔야 합니다. text파일 정보는 [1] 에 포스팅 해두었습니다.



assets 폴더가 없으시면 생성하시면 됩니다.



StationInfo.java
public class StationInfo implements Serializable{
    String st_station;
    String st_stationCode;

    public StationInfo(String st_station, String st_stationCode) {
        this.st_station = st_station;
        this.st_stationCode = st_stationCode;
    }
}


코드와 역을 저장할 Class

StationListActivity의 Layout
activity_station.xml


    

    



StationListActivity.java
public class StationListActivity extends AppCompatActivity {

    EditText et_searchStation;  // 초성검색을 위한 EditText
    ListView lv_stationList;   // Text-> App 올리기위한 LsitView
    ArrayList mInfo; // ListView만들기 위한 ArrayList
    AssetManager mag;    // assets 폴더에서 올리기 위한 Manager 
    String name_buffer;
    String code_buffer;
    StationListAdapter mAdapter; // Adapter
    Intent mResult;   // 결과물 Return 할 Intent

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_station);
        onInit();
        ReadStation();
        mAdapter = new StationListAdapter(this,android.R.layout.simple_list_item_1, mInfo);
        lv_stationList.setAdapter(mAdapter);

    }
    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case HandlerMessage.THREAD_HANDLER_SUCCESS_INFO:
                    mResult = getIntent();
                    StationInfo tResult = (StationInfo)msg.obj;
                    mResult.putExtra("stationInfo", tResult);
                    setResult(RESULT_OK,mResult);
                    finish();
                    break;
                default:
                    break;
            }
        }
    };




    void onInit()
    {
        mag = this.getResources().getAssets();   // Assets 초기화
        mInfo = new ArrayList<>();
        et_searchStation = (EditText)findViewById(R.id.et_stationSearch);
        et_searchStation.addTextChangedListener((new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) { // 초성검색을 위한 함수, L
                String temp = et_searchStation.getText().toString();
                mAdapter.Filter(temp); // 단어 입력시 Filter 함수에 접근,
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        }));
        lv_stationList = (ListView)findViewById(R.id.lv_stationList);
    }

    void ReadStation()
    {
        try {
            InputStream name = mag.open("InfoTrainStation.txt");
            InputStream code = mag.open("InfoTrainStationCode.txt");
            int nameSize = name.available();
            int codeSize = code.available();

            byte[] buffer = new byte[nameSize];
            byte[] buffer2 = new byte[codeSize];

            name.read(buffer);
            code.read(buffer2);

            name.close();
            code.close();

            name_buffer = new String(buffer);  // byte -> string 변환
            code_buffer = new String(buffer2);

            StringToToken(name_buffer,code_buffer); // 단어별로 쪼갬
        }catch(IOException e) {
            throw new RuntimeException(e);
        }


    }
    void StringToToken(String name,String code)
    {
        StringTokenizer mToken = new StringTokenizer(name, "\r\n"); // Enter 기준으로 쪼갭니다.
        StringTokenizer mToken2 = new StringTokenizer(code, "\r\n");

        while(mToken.hasMoreTokens() )
        {
            String mName = mToken.nextToken();
            String mCode = mToken2.nextToken();

            StationInfo temp = new StationInfo(mName+"역",mCode); // 만들어진 것을 Class 화
            mInfo.add(temp); // List추가
        }
    }
}



다음은 ListView를 사용할 Adapter입니다.


ListView의 아이템 layout으로는 다음과 같습니다.
trainitem1.xml




    

    





초성검색은 다음 사이트 http://www.androidpub.com/1473929 에서 참고를 하였습니다.

StationListAdapter
public class StationListAdapter extends ArrayAdapter implements View.OnClickListener {
    private static final char HANGUL_BEGIN_UNICODE = 44032; // 가
    private static final char HANGUL_LAST_UNICODE = 55203; // 힣
    private static final char HANGUL_BASE_UNIT = 588;//각자음 마다 가지는 글자수
    //자음
    private static final char[] INITIAL_SOUND = { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };

    ArrayList mData;
    ArrayList mTemp;
    Context mContext;
    StationListActivity mStationActivity;
    public StationListAdapter(Context context, int resource, ArrayList objects) {
        super(context, resource, objects);
        mData = objects; // 데이터 받음
        mStationActivity = (StationListActivity)context;
        mContext = context;
        Sort_String(); // ㄱㄴㄷ 순으로 데이터 정렬 
        mTemp = new ArrayList<>(); 
        mTemp.addAll(mData); // 데이터 복사

    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        parent.invalidate();
        View v = convertView;

        final int set = position;
        if (v == null) {
            LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.trainitem1, null);
        }
        v.setTag(position);
        v.setOnClickListener(this);

        if(mData.size() <= position) return v; // 혹시모를 오류 예외처리.

        final StationInfo p = mData.get(position);
        if (p != null) {
            TextView tv = (TextView) v.findViewById(R.id.tv_stationList);

            if (tv != null) {
                tv.setText(p.st_station);
            }

        }
        return v;
    }


    @Override
    public void onClick(View v) { // ListView에 클릭 발생시, 메세지 전달 
        Message msg = new Message();
        int position = (int)v.getTag();
         msg.obj = mData.get(position);
         msg.what = HandlerMessage.THREAD_HANDLER_SUCCESS_INFO;
         mStationActivity.handler.sendMessage(msg);

    }

    public void Filter(String data)
    {
        if(data.length() == 0) {
            mData.clear();
            mData.addAll(mTemp);
            Sort_String();
            return ;
        }
        mData.clear(); // 모두 삭제, 
        for(StationInfo temp : mTemp) // mTemp에 복제해둔 데이터 하나씩 불러옴
        {
            if(matchString(temp.st_station,data)) // 초성에 해당하면, 데이터를 더한다.
                mData.add(temp);
        }

        Sort_String(); // 데이터를 다시 정렬
        notifyDataSetChanged(); // 변화 알림
    }
    private  boolean isInitialSound(char searchar){
        for(char c:INITIAL_SOUND){
            if(c == searchar){
                return true;
            }
        }
        return false;
    }

    public  boolean matchString(String value, String search){
        int t = 0;
        int seof = value.length() - search.length();
        int slen = search.length();
        if(seof < 0)
            return false; //검색어가 더 길면 false를 리턴한다.
        for(int i = 0;i <= seof;i++){
            t = 0;
            while(t < slen){
                if(isInitialSound(search.charAt(t))==true && isHangul(value.charAt(i+t))){
                    //만약 현재 char이 초성이고 value가 한글이면
                    if(getInitialSound(value.charAt(i+t))==search.charAt(t))
                        //각각의 초성끼리 같은지 비교한다
                        t++;
                    else
                        break;
                } else {
                    //char이 초성이 아니라면
                    if(value.charAt(i+t)==search.charAt(t))
                        //그냥 같은지 비교한다.
                        t++;
                    else
                        break;
                }
            }
            if(t == slen)
                return true; //모두 일치한 결과를 찾으면 true를 리턴한다.
        }
        return false; //일치하는 것을 찾지 못했으면 false를 리턴한다.
    }

    private  boolean isHangul(char c) { // 한글인지 체크, 
        return HANGUL_BEGIN_UNICODE <= c && c <= HANGUL_LAST_UNICODE;
    }

    private  char getInitialSound(char c) {       // char 가 한글이라면, 초성을 찾는다.
        int hanBegin = (c - HANGUL_BEGIN_UNICODE);
        int index = hanBegin / HANGUL_BASE_UNIT;
        return INITIAL_SOUND[index];
    }

    void Sort_String() // 한글, 영어 공통 적용됩니다. 영어라면 ABC순일테고, 한글이면 ㄱㄴㄷ 순입니다. comparator는 정확히 공부가 되지 않아, 참고를 하여 작성하였습니다.
    {
        final Comparator myComparator= new Comparator() {
            private final Collator collator = Collator.getInstance();
            @Override
            public int compare(StationInfo object1,StationInfo object2) {
                return collator.compare(object1.st_station, object2.st_station);
            }
        };
        // Collections.sort 로 comparator 를 주어서 데이터를 정렬 시킨다.
        Collections.sort(mData, myComparator); // 정렬시킵니다.
    }
}



이런식으로 Adapter가 구성됩니다.


이를 통해 위의 화면에 접근할 수 있습니다.




다음은 MainActivity간 Intent 통신 결과 입니다.
MainActivity.java
@Override
    public void onClick(View v) {
        Intent intent;
        switch(v.getId())
        {
            case R.id.btn_endStation:
                intent = new Intent(this,StationListActivity.class);
                startActivityForResult(intent,HandlerMessage.END_SET); // requstCode를 설정합니다.
                break;
            case R.id.btn_startStation:
                intent = new Intent(this,StationListActivity.class);
                startActivityForResult(intent,HandlerMessage.START_SET); // requstCode를 설정합니다.
                break;
            case R.id.btn_SearchTicket:
                mRequest = new URLRequest(mStartStation,mEndStation,mThis);
                mRequest.run();
                dialog = ProgressDialog.show(this, "",
                        "Loading", true);

                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch(requestCode)
        {
            case HandlerMessage.START_SET: //StationListActivity 종료 시 Request Code로 구분합니다.
                if(data != null)
                    mStartStation = (StationInfo)data.getSerializableExtra("stationInfo");

                tv_startStationName.setText(mStartStation.st_station);
                break;
            case HandlerMessage.END_SET:
                if(data !=null)
                    mEndStation = (StationInfo)data.getSerializableExtra("stationInfo");

                tv_endStationName.setText(mEndStation.st_station);
                break;
        }
    }



이것이 완료되면 다음과 결과화면을 얻을 수 있습니다.



공공데이터 기차역 정보 조회 서비스 API 사용기차역 조회 결과기차역 조회 결과





이상 포스팅을 마치겠습니다.


전체 파일은 깃허브 사이트에 업로드 하였습니다.


주소는 https://github.com/warguss/TicketApiKorea 입니다.

추가로 읽으면 좋을 것

댓글

댓글 쓰기

이 블로그의 인기 게시물

윤석열 계엄령 선포! 방산주 대폭발? 관련주 투자 전략 완벽 분석

대통령 퇴진운동 관련주: 방송·통신·촛불수혜주 완벽 분석

키움 OPEN API MFC 개발 (1)