[Android] 맨날 까먹는 runOnUiThread 사용예

통신을 통해 데이터를 스레드를 통해 딱… 가져오고 난 후 동일한 스레드에서 바로 Spinner 등과 같은 UI에 데이터를 설정하시면 우에엑~~ 하고 구토를 하시는 안드로이드님.

UI 단에 데이터를 설정할 수 있는 권한은 오직 UI 스레드, 즉 메인 스레드만이 하실 수 있는 전지전능하신 권한이므로 감히 자식 스레드에서는 기도를 통해 소원을 빌어야 합니다. 안드로이드님께서는 이러한 기도를 runOnUiThread라는 Context의 매서드를 통해 가능하게 하옵시고..

아래와 같은 예제를 통해 기도 방법을 메모하고자 합니다.

String strJSON = event.getResultJSON();

if(strJSON != null) {
    if(event.getRequestId().compareTo("행정구") == 0) {
        JSONArray array = (JSONArray)JSONValue.parse(strJSON);
        Iterator iter = array.iterator();
				
        final ArrayList list = new ArrayList();
        
        while(iter.hasNext()) {
            JSONObject obj = (JSONObject)iter.next();
            Set keySet = obj.keySet();
            Iterator keyIter = keySet.iterator();
            while(keyIter.hasNext()) {
            Object key = keyIter.next();
            Object val = obj.get(key);
            list.add(val);
        }
    }	
				
    Collections.sort(list);
				
    this.runOnUiThread(
        new Runnable() {
            @Override
            public void run() {
                Spinner spGuName = (Spinner)findViewById(R.id.spGuName);
                ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(
                    ActivitySearchLocation.this, 
                    R.layout.custom_simple_dropdown_item_1line, list.toArray()
                );
                spGuName.setAdapter(adapter);						
            }
        });
    }
}

위의 코드는 데이터를 가져오는 통신 스레드에서 실행되는 코드이고 23번째 줄이 바로 ‘기도’에 해당합니다. 정작 중요한 내용보다 부수적인 내용이 더 많은 코드이지만.. 나중을 위해 Keep 해 둡니다. 참고로 가져온 데이터를 배열로 저장하고 이를 Collections 클래스의 정적 함수인 sort를 통해 정렬하고 있습니다.

[Andorid] 항상 까먹는 Spinner 항목값 설정

안드로이드는 뭐 하나 하려면 제법 손이 많이 갑니다.. 스피너에 항목값 리스트를 할당하는 것도 다른 툴에 비해 갑절이나 많은 코드를 작성해야 합니다.

String[] facilityList = { 
    "공급배관", "사용자배관", "공급밸브", "정압기", "테스트박스", 
    "로케이트박스", "정류기", "배류기", "수취기", "검지공" 
};
		
Spinner spFacilityType = (Spinner)findViewById(R.id.spFacilityType);
ArrayAdapter<string> adapter = new ArrayAdapter<string>(
    this, 
    android.R.layout.simple_spinner_dropdown_item, 
    facilityList);
spFacilityType.setAdapter(adapter);
spFacilityType.setSelection(0);

그런데 말입니다… 이렇게 해서 만들어진 스피너의 생김새가 다소 촌스럽습니다. 바로 아래처럼…

사용자 삽입 이미지

표시되는 항목들의 높이가 너무 높아 보이지 않나요? 이 항목의 높이값을 조절하기 위해 위의 코드 중 9번줄의 코드를 변경했는데 전체 코드를 다시 보면 다음과 같습니다.

String[] facilityList = { 
    "공급배관", "사용자배관", "공급밸브", "정압기", "테스트박스", 
    "로케이트박스", "정류기", "배류기", "수취기", "검지공" 
};
		
Spinner spFacilityType = (Spinner)findViewById(R.id.spFacilityType);
ArrayAdapter<string> adapter = new ArrayAdapter<string>(
    this, 
    R.layout.custom_simple_dropdown_item_1line, 
    facilityList);
spFacilityType.setAdapter(adapter);
spFacilityType.setSelection(0);

결과는 다음과 같습니다

사용자 삽입 이미지

이를 위해 여기서 추가한 리소스가 하나 있습니다. 바로 custom_simple_dropdown_item_1line.xml인데요. 그 내용은 다음과 같습니다.



이상 맨날 까먹어 고생하는 코드를 정리해 보았습니다.

[Android] ViewFlipper에서 View의 전환에 대한 좌우 애니메이션

ViewFlipper을 사용해 여러 개의 뷰를 전환할 수 있는데요. 기본적으로 제공되는 안드로이드에서는 뷰의 전환이 왼쪽에서 오른쪽으로만 됩니다. 이는 안드로이드에서 제공하는 애니메이션 리소스가 왼쪽에서 오른쪽으로 전환되는 것만을 제공하기 때문입니다. 이 애니메이션 리소스는 android.R.anim.slide_in_left와 android.R.anim.slide_out_right입니다.

여기서 오른쪽에서 왼쪽으로 뷰의 전환이 이뤄지는 애니메이션을 위해서는 slide_in_right.xml와 slide_out_left.xml을 직접 추가하였고 해당 파일의 내용은 첨부 파일을 참고하시기 바랍니다.

실행 결과는 아래와 같습니다.

끝으로 전체 소스 코드를 다운로드 받을 수 있는 URL은 아래와 같습니다.

[Android] 스레드에서 ProgressDialog 사용

ProgressDialog가 사용되는 경우는 대부분 시간이 많이 소요되는 연산을 스레드에서 실행할때 사용자에게 대기하도록 하는 용도입니다. 그러나 이 글의 제목에서 언급된 스레드는 ProgressDialog를 생성하고 표시(Show)하는 코드가 스레드에 위치하는 경우를 의미합니다.

블랙포인트라는 모바일 GIS 엔진에서 지도를 처음 그리기 시작할때 발생하는 이벤트와 지도가 모두 다 그려질때 발생하는 이벤트의 리스너는 각각 OnBeforeMapDrawEventListener와 OnUpdateMapCompletedEventListener입니다. 이 이벤트 리스너가 호출되는 위치가 바로 그리기 스레드(Rendering Thread)이므로 Handler를 사용하여 ProgressDialog를 생성하고 표시해줘야 합니다.

private static ProgressDialog progressDialog = null;

@Override
public void onBeforeMapDraw(BeforeMapDrawEvent event) {
    Message msg = new Message();
    msg.what = 0;
    msg.obj = event;
    handler.sendMessage(msg);
}
	
@Override
public void onUpdateMapCompleted(UpdateMapCompletedEvent event) {
    handler.sendEmptyMessage(1);
}
	
private static Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        if(msg.what == 0) {
            BeforeMapDrawEvent event = (BeforeMapDrawEvent)msg.obj;
            if(event.isCalledUpdateMethod()) {
                if(MainActivity.progressDialog == null)  {
                    MainActivity.progressDialog = ProgressDialog.show(
                        event.getMap().getContext(), "", 
                        Html.fromHtml(
                            "Please wait for map drawing ..."));
                    Window dlgWin = MainActivity.progressDialog.getWindow()
                    dlgWin.setGravity(Gravity.BOTTOM);
                }
            }				
        } else if(msg.what == 1) {
            if(MainActivity.progressDialog != null) { 
                MainActivity.progressDialog.hide();
                MainActivity.progressDialog = null;
            }				
        }
    }
};

스레드로부터 안전한 ProgressDialog를 표시하는 중요한 코드는 16번 코드부터입니다. 아래는 위의 코드가 적용되어 실제 실행되는 화면입니다.

사용자 삽입 이미지