본문 바로가기

프로젝트/블루투스 무드등

[비전공자도 만들 수 있는 블루투스 무드등] 13. 안드로이드 앱 part 3-1 with 비트맵(Bitmap)

안녕하세요? 닉네임간편입니다. 이번 시간부터는 앞서 레이아웃에서 정의했던 뷰들을 참조해 필요한 기능을 수행하도록 만들어보겠습니다.


전체 소스는 여기에 있습니다.

https://github.com/creativeduck/MyLED

앱을 미리 사용해보고 계신 분들은, 이 링크를 타고 설치해주시면 됩니다.

https://play.google.com/store/apps/details?id=com.mybest.myled


1. 변수 정의

ImageView colorPick; // 색상 선택하는 이미지
ShapeableImageView selectedColor; // 선택된 색상 보여주는 이미지
TextView currentState; // 현재 색상과 밝기 표시할 텍스트뷰
SeekBar bar; // 밝기 조절할 시크바
ImageButton btnRainbow; // 레인보우 버튼
ImageButton btnPower; // 전원 버튼
ImageButton btnColorPicker; // 컬러피커 버튼
ImageButton btnConnect; // 블루투스 접속 버튼

boolean powerOn = false; // 전원이 켜졌는지 여부

 

앞서 레이아웃에서 만든 모든 위젯 변수들을 정의합니다. 이때 변수들의 이름은 해당 뷰의 아이디와 동일하게 작성했습니다.

 

또한 boolean 자료형 변수 powerOn을 정의하고 초기화합니다. 이는 전원이 켜져 있는지 여부를 판단하는 변수입니다.

 

btnPower = findViewById(R.id.btnPower);
selectedColor =  findViewById(R.id.selectedColor);
colorPick = findViewById(R.id.colorPick);
btnRainbow = findViewById(R.id.btnRainbow);
currentState = findViewById(R.id.currentState);
bar = findViewById(R.id.bar);
btnConnect = findViewById(R.id.btnReconnect);
btnColorPicker = findViewById(R.id.btnColorPicker);

 

이후 onCreate() 메서드 내부에서 findViewById() 메서드를 이용해 레이아웃의 뷰를 변수가 참조하도록 합니다.

findViewById() 메서드는 레이아웃에서 정의한 id를 파라미터로 전달받으며, 이 id에 해당하는 뷰를 찾아 가져옵니다. 이렇게 해야 메인 액티비티의 자바 코드 내에서 뷰를 사용할 수 있기 때문에 꼭 해주어야 하는 과정입니다.

 

이제 변수를 모두 만들어주었으니, 해당 변수가 원하는 기능을 동작하도록 만들겠습니다.

 

2. 색상 선택 이미지뷰

앞서 정의한 colorPick 이미지뷰는 다음과 같습니다.

 

이 이미지뷰에 원하는 색을 터치하면 해당 색의 불빛이 아두이노에 연결된 LED 스트립에서 나오도록 하는 기능을 추가하겠습니다.

 

colorPick.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                
                final int evX = (int) event.getX();
                final int evY = (int) event.getY();

                Bitmap mBitmap = getBitmapFromView(colorPick);

                try {
                    int pixel = mBitmap.getPixel(evX, evY);
                    sendData(pixel);
                    powerOn=true; 
                }catch (IllegalStateException e){
                    e.printStackTrace();
                } catch(IllegalArgumentException e) {
                    e.printStackTrace();
                }
                mBitmap.recycle();
                return true;
            }
        });

 

1) 터치 리스너 설정

colorPick 이미지뷰에 setOnTouchListener() 메서드를 통해 터치 리스너를 설정합니다.

그리고 onTouch() 메서드를 재정의해서 이미지뷰가 터치될 때 동작을 수행하도록 합니다.

 

2) 터치된 곳의 좌표 가져오기

event.getX()와 event.getY() 메서드는 터치된 곳의 x, y 좌표를 반환합니다. 이를 evX, evY가 각각 참조하도록 합니다.

final 키워드로 선언한 변수는 이후에 값을 변경할 수 없습니다. evX, evY가 무조건 터치된 곳의 좌표값을 가져야 하므로, final 키워드를 선언해 변수를 생성했습니다.

 

3) 비트맵 객체 생성

getBitmapFromView() 메서드를 통해 colorPick 뷰로부터 비트맵 객체를 만들어 가져옵니다.

 

4) try-catch 구문

getPixel() 메서드는 파라미터로 전달된 x, y좌표에 해당하는 곳의 색상 객체를 반환합니다.

앞서 만들었던 비트맵 객체에서 evX, evY에 해당하는 좌표의 픽셀 값을 가져옵니다.
이때 주의할 사항이 있습니다.

a) 만일 getPixel() 메서드에 전달된 x, y 값이 비트맵(여기선 imgbmp)의 범위를 초과하거나 혹은 음의 값이라면 IllegalArgumentException 예외가 발생합니다.


IllegalArgumentException는 부적절한 파라미터를 메서드에 전달할 때 발생하는 예외입니다.
즉, 전달된 x, y 좌표가 이미지의 가로, 세로보다 크거나 혹은 0보다 작다면 부적절한 파라미터를 전달한 것이기 때문에 이 예외가 발생하는 것입니다.

b) 만일 비트맵 configurations가 Bitmap.Config.HARDWARE라면 IllegalStateException 예외가 발생합니다.

 

비트맵 configuratinos는 픽셀들이 어떻게 저장되는지를 나타냅니다.
만일 이것이 Bitmap.Config ARGB_8888이라면, 각각의 픽셀은 4바이트로 저장됩니다.

이때 RGB 색상과 투명도를 위한 알파 값은 각각 8비트의 정확도로 저장됩니다. 8비트는 0~255까지의 값을 가지며, 따라서 이 경우 색의 정확도가 높은 수준으로 픽셀이 저장된다고 할 수 있습니다.

반대로 Bitmap.Config HARDWARE이라면 비트맵은 오직 그래픽 메모리에만 저장되며, 변경이 불가능합니다. 이것은 오직 단지 화면에 비트맵을 그릴 때만 최적입니다.


즉, getPixel() 메서드가 호출되는 비트맵 객체는 픽셀이 저장되는 방식이 Bitmap.Config HARDWARE이면 예외가 발생한다는 것입니다.

IllegalStateException 예외는 메서드가 부적절하거나 불법인 상황에 호출되는 것입니다.
바꿔 말하면, 해당 기능이 수행될 적절한 상황이 아니라는 것을 의미합니다.
즉, getPixel() 메서드가 호출되는 비트맵 객체의 configurations가 Bitmap.Config HARDWARE인 것은, 해당 메서드가 수행될 적절한 상황이 아니므로 이 예외를 발생시키는 것입니다.

예외가 발생할 수 있다면 try-catch 구문을 통해 예외처리를 해주어야 합니다. 따라서 try 구문 안에 해당 메서드를 넣고 catch 구문을 통해 예외를 처리해줍니다.

 

5) 데이터 전송 메서드 및 전원 켜짐

sendData() 메서드를 통해 픽셀 데이터를 아두이노로 전송합니다.

이후 powerOn을 true로 설정해 전원 켜짐으로 설정합니다.

 

6) recycle()

이 메서드를 호출하면 해당 비트맵 객체를 참조한 변수의 메모리를 해제하고, 비트맵 객체의 픽셀 데이터에 대한 참조를 제거합니다. 즉, 아무것도 이 비트맵 객체의 픽셀 데이터를 참조하지 않게 만듭니다. 이렇게 되면 가비지 콜렉터가 참조가 없는 것들을 제거할 때 이 데이터를 제거하도록 만들어줍니다.
즉, 실제 데이터를 제거하는 건 아니지만 제거를 유도합니다.
이 함수는 번복할 수 없으므로 오직 해당 비트맵을 더 이상 사용하지 않을 때에만 호출해야 합니다.

 

마지막으로 onTouch() 메서드는 boolean 자료형을 반환하므로, 이 메서드에선 true를 반환하도록 합니다.

 

2. 뷰로부터 비트맵 객체 생성 메서드

public Bitmap getBitmapFromView(View view) {
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        return bitmap;
    }


1) 비트맵 객체 생성
파라미터로 전달되는 뷰의 가로와 세로 크기만큼의 비트맵 객체를 생성합니다. 본 예제를 예로 들면 colorPick 이미지뷰의 가로, 세로 크기만큼의 비트맵 객체를 생성한다.
이때 앞서 설명한 비트맵의 configurations를 Bitmap.Config ARGB_8888로 설정합니다.

2) 캔버스 객체 생성

앞서 만든 비트맵 객체가 설정된 캔버스 객체를 생성합니다. 
설정된 비트맵 객체는 캔버스 객체의 그림판 역할을 수행하며, 캔버스로 무언가를 그리면 이것이 비트맵 객체에 반영되어 나타납니다.

3) 뷰 렌더링
draw() 메서드를 통해 캔버스 객체에 뷰를 렌더링 합니다. 쉽게 말하면, 캔버스 객체에 뷰를 그립니다.
예제를 통해 설명하면, 캔버스 객체에 colorPick 이미지뷰를 그립니다.

이후 이것이 비트맵 객체에 반영되어 비트맵 객체는 colorPick에 해당하는 이미지가 그려집니다.

 

4) 비트맵 객체 반환

반환된 비트맵 객체는 위에서 봤던 코드에서처럼 색상을 추출하는 데에 사용됩니다.

결과적으로 이미지뷰 자체에서 색상을 추출하는 것이 아니라 이미지뷰가 캔버스에 먼저 그려지고, 이것이 반영된 비트맵 객체로부터 색상을 추출하는 것입니다.

 

3. 마무리

이번 시간에는 이미지뷰에 대해 다루어봤습니다. 나머지 뷰들에 대해선 다음 시간에 다루도록 하겠습니다. 읽어주셔서 감사합니다. 오늘 알려드린 정보도 많은 도움이 되었으면 좋겠습니다.

728x90
반응형