프로젝트

[안드로이드 스튜디오] 엑셀 파일을 내부DB에 저장 후 불러오기

gom1n 2021. 8. 24. 17:08

엑셀 파일이 존재해야 하고, xls 형식이어야 한다.

 

1. assets 폴더를 생성해 엑셀파일(xls)을 넣는다.

2. 내부 DB (SQLite)에 엑셀 내용들을 저장한다.

3. Cursor를 이용해 엑셀 내용들을 읽어온다.

4.(추가구현) 구글맵에 마커 표시한다.

*구글맵 마커 표시는 제 개인 프로젝트 겸 해본 것입니다.


1. 엑셀파일 삽입

- assets 폴더 생성하는 법

프로젝트 폴더에 대고 우클릭한다.

 

- 공공데이터 포털에서 다운로드받은 엑셀파일이다.


2. 내부 DB에 엑셀내용 저장

참고로 내 엑셀파일은 이런 식으로 생겼다.

번호 / 약국이름 / 읍면 / 주소 / 전화번호

 

2-1) DatabaseHelper 클래스 생성

public class DatabaseHelper extends SQLiteOpenHelper {

    public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table IF NOT EXISTS incheon_medicine  ("
                + "_id integer primary key autoincrement,"
                + "번호 INTEGER," + "약국 TEXT," + "읍면 TEXT," + "주소 TEXT," + "전화번호 INTEGER);";
        
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        String sql = "DROP TABLE IF EXISTS incheon_medicine";

        db.execSQL(sql);
        onCreate(db);
    }

}

 

2-2) setDatas() 함수 생성

앞서, 우선 내부DB를 읽고 쓰게 할 수 있는 permission을 선언한다.

그리고 참고로, 나는 fragment 를 사용했기 때문에 코드가 살짝 다를 수 있다. 적당히 변환시켜주자.

public void setDatas() {
        ContentValues values = new ContentValues();
        Workbook workbook = null;
        Sheet sheet = null;

        //db파일 생성
        dh = new DatabaseHelper(this.getActivity(), "incheon_medicine.db", null, 1);
        db = dh.getWritableDatabase();

        //파일 불러와서 db에 저장하기
        try {
            InputStream is = getActivity().getBaseContext().getResources().getAssets().open("incheon_medicine.xls");
            workbook = Workbook.getWorkbook(is);

            if (workbook != null) {
                sheet = workbook.getSheet(0);

                if (sheet != null) {

                    int colTotal = sheet.getColumns();
                    int rowIndexStart = 1;
                    int rowTotal = sheet.getColumn(colTotal - 1).length;

                    for (int row = rowIndexStart; row < rowTotal; row++) {

                        for (int col = 0; col < colTotal; col++) {
                            String contents = sheet.getCell(col, row).getContents();

                            switch (col) {
                                case 0:
                                    values.put("번호", contents);
                                    break;
                                case 1:
                                    values.put("약국", contents);
                                    break;
                                case 2:
                                    values.put("읍면", contents);
                                    break;
                                case 3:
                                    values.put("주소", contents);
                                    break;
                                case 4:
                                    values.put("전화번호", contents);
                                    break;
                            }
                        }
                        //db의 incheon_medicine 테이블에 values 값 넣어줌
                        db.insert("incheon_medicine", null, values);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3. 내부 DB의 ___테이블의 내용을 읽어온다. - ReadDatas 함수

커서를 이용해 옆으로 읽어오면서 번호/약국이름/읍면/주소/전화번호를 가져온다.

public void ReadDatas(GoogleMap googleMap) {
        Context context = this.getContext();
        //테이블 incheon_medicine를 읽음
        db = dh.getReadableDatabase();
        Cursor c = db.query("incheon_medicine", null, null, null, null, null, null);

        while(c.moveToNext()) {
            Integer num = c.getInt(c.getColumnIndex("번호"));
            String name = c.getString(c.getColumnIndex("약국"));
            String town = c.getString(c.getColumnIndex("읍면"));
            String address = c.getString(c.getColumnIndex("주소"));
            Integer phoneNum = c.getInt(c.getColumnIndex("전화번호"));

            Location location = addrToPoint(context, name);
            LatLng loc = new LatLng(location.getLatitude(), location.getLongitude());
            MarkerOptions marker = new MarkerOptions();
            marker.position(loc); //마커 위치
            marker.title(name);

			// 마커 이미지 설정
            BitmapDrawable bitmapdraw = (BitmapDrawable) getResources().getDrawable(R.drawable.med);
            Bitmap b = bitmapdraw.getBitmap();
            Bitmap smallMarker = Bitmap.createScaledBitmap(b, 200, 200, false);
            marker.icon(BitmapDescriptorFactory.fromBitmap(smallMarker));

            googleMap.addMarker(marker).showInfoWindow();

        }
        c.moveToFirst();
    }

4. (추가구현) 구글맵에 마커표시

여기서 살짝의 난관이 있었다....

GoogleMap 안에서 기존의 LatLng 객체를 사용하려면 경도와 위도를 구해와야하는데, 

너무나도 양이 많고 귀찮은 것이다.

그래서 주소내용만으로 위도경도를 알아낼 수 있는 geocoding 을 사용하였다.

geocoder는, 주소내용을 가지고 __가지 위도/경도를 찾아내 array에 저장하는 방식이다. (나는 3가지로 함)

이를 위해서는 안드로이드 스튜디오 내 인터넷 연결이 필수이다.

// geocoding 주소 변환
    public static Location addrToPoint(Context context, String place) {
        Location location = new Location("");
        Geocoder geocoder = new Geocoder(context);
        List<Address> addresses = null;

        try {
            addresses = geocoder.getFromLocationName(place,3);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(addresses != null) {
            for(int i = 0 ; i < addresses.size() ; i++) {
                Address lating = addresses.get(i);
                location.setLatitude(lating.getLatitude());
                location.setLongitude(lating.getLongitude());
            }
        }
        return location;
    }

 

OnMapReady 함수는 다음과 같다.

일단 서울로 초기 화면을 잡아주었다.

	//맵뷰 설정
    @Override
    public void onMapReady(GoogleMap googleMap) {

        Context context = this.getContext();
        //마커찍기(위도,경도)
        ReadDatas(googleMap);
        
        //서울 찍기(중심)
        Location seoul = addrToPoint(context, "서울시청");
        LatLng SEOUL = new LatLng(seoul.getLatitude(), seoul.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(SEOUL);
        markerOptions.title("서울");
        markerOptions.snippet("서울시청");
        googleMap.addMarker(markerOptions);

        //인포윈도우 클릭
        googleMap.setOnInfoWindowClickListener(this);
        //맵뷰 카메라위치, 줌 설정 (서울)
        googleMap.moveCamera(CameraUpdateFactory.newLatLng(SEOUL));
        googleMap.animateCamera(CameraUpdateFactory.zoomTo(10));
        googleMap.getUiSettings().setZoomControlsEnabled(true);
    }

이렇게 하면 어찌저찌 마커가 표시되긴 하는데, 아주 느리다.

그리고 메인 쓰레드에서 너무 많은 작업을 한다며 안드로이드 스튜디오에서 불평을 한다.ㅋㅋ

 

결과화면) 인천 내 폐의약품 수거함이 있는 약국 위치 표시

마커이미지는 내가 그린 것이 아니다. 팀원이...

몇 개는 서울 쪽에 표시되는 거 보면... geocode가 완벽한 위도/경도를 잡아내는 것은 아닌 듯 하다.

오래 걸리는 시간이 내부DB를 읽어오는 것 때문인 지, geocoding 때문인 지 정확히는 모르겠으나,

아마 geocoding 때문 같다.

(내부 DB만 읽어오는 것 자체는 그리 오래 걸리지 않았던 기억이 있다.)