Android - 새로운 뷰 만들기

유의선·2023년 7월 5일
0

API에서 제공하는 위젯을 사용하면 대부분의 화면을 만들 수 있다. 하지만 개발자가 원하는 기능을 가진 위젯을 따로 구상해야 할 때는 새로운 뷰나 위젯을 직접 만들 필요가 있다.


API에서 제공하는 뷰를 사용하려면 API의 뷰를 상속해야 한다. 그리고 API의 뷰를 상속하여 새로운 뷰를 만들 때는 뷰가 그려지는 방법을 이해해야 한다.

뷰의 영역과 크기는 그 뷰를 포함하고 있는 레이아웃의 영향을 받아 정해진다. 이 때 개발자가 뷰의 상태에 따라 추가적인 코드를 넣을 수 있도록 콜백 메소드가 호출된다.

뷰가 스스로 크기를 정할 때 자동으로 호출되는 메소드는 onMeasure 메소드이다.

public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

onMeasure 메소드의 파라미터로 전달되는 widthMeasureSpec과 heightMeasureSpec은 이 뷰를 담고 있는 레이아웃에서 이 뷰에게 허용하는 여유 공간의 폭과 넓이이다.
즉 부모 컨테이너에서 여유 공간에 대한 정보를 전달하는데 이 값을 참조하여 뷰가 보일 적절한 크기를 반환하면 이 크기 값으로 뷰가 그려지게 된다.

onMeasure 메소드 안에서 이 뷰를 담고 있는 레이아웃에게 이 뷰의 크기 값을 반환하고 싶다면 다음 메소드를 사용한다.

void setMeasuredDimension(int measuredWidth, int measuredHeight)

이 메소드의 두 파라미터는 뷰의 폭과 높이 값이 된다.


뷰가 화면에 보일 때는 onDraw 메소드가 호출된다

public void onDraw(Canvas canvas)

새로운 뷰를 클래스로 정의하고 그 안에 onDraw 메소드를 다시 정의한 후 필요한 코드를 넣어 기능을 구현하면 다른 모양으로 보이는 뷰를 직접 만들 수 있다.

또한 손가락으로 터치하여 화면에 그려진 뷰를 이동하려고 할 때는 뷰가 이동한 후에 그 뷰의 그래픽을 다시 그려야 할 필요가 있다.
이때는 invalidate 메소드를 호출하면 자동으로 onDraw 메소드가 다시 호출되어 이동한 좌표에 뷰의 그래픽을 다시 그리도록 만들 수 있다.


새로운 뷰 만들기

새 클래스 MyButton.java를 만든다.

AppCompatButton을 상속하도록 만들고,
생성자를 두 개 만들고,
init 메소드를 만들어 뷰가 초기화되도록 만들었다.

public class MyButtom extends AppCompatButton {
    public MyButtom(@NonNull Context context) {
        super(context);
    }

    public MyButtom(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    private void init(Context context){
        setBackgroundColor(Color.CYAN);
        setTextColor(Color.BLACK);

        float textSize = getResources().getDimension(R.dimen.text_size);
        setTextSize(textSize);
    }
}

init 메소드에서 텍스트의 크기를 정할 때 사용한 R.diem.text_size를 만든다

value 폴더 안에 dimens.xml 파일을 만들고 코드를 추가한다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">16sp</dimen>
</resources>

dimens.xml 파일은 크기 값 등을 정의할 수 있는 파일이다.

이제 MyButton.java의 onDraw 메소드와 onTouchEvent 메소드를 재정의한다.


	...

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Log.d("MyButton", "onDraw 호출됨.");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("MyButton", "onTouchEvent 호출됨");

        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                setBackgroundColor(Color.BLUE);
                setTextColor(Color.RED);
                break;
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                setBackgroundColor(Color.CYAN);
                setTextColor(Color.BLACK);
                break;
        }

        invalidate();
        return true;
    }
    
    ...
    

onDraw 메소드와 onTouchEvent 메소드가 언제 호출되는지 알아보기 위해 로그를 출력해도록 했다.

onTouchEvent 메소드가 호출되면서 전달되는 MotionEvent 객체에는 getAction 메소드가 있어서 손가락이 눌렸는지, 눌린 상태로 드래그되는지 등의 상태를 알 수 있다.
이 getAction 메소드로 상태를 확인해서 버튼을 누르면 색이 변하도록, 눌린 상태가 끝나면 원래대로 색이 돌아오도록 하였다.

상태가 변하였다면 invalide 메소드를 호출하여 뷰를 다시 그린다.


새로 정의한 버튼을 XML 레이아웃에 추가한다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <org.techtown.view.MyButtom
        android:id="@+id/button"
        android:layout_width="200dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        android:text="시작하기"/>
</RelativeLayout>



0개의 댓글