[Android] 안드로이드

[ 안드로이드 기본 ] 터치로 움직이고 중력이 작용하는 CutomView 만들기

개발자혜콩 2026. 4. 15. 10:45

1. CustomViewTest01

터치로 움직이고 중력이 작용하는 로봇 뷰 만들기

목표: 커스텀 뷰를 생성하여 터치한 위치로 이동하고, 가만히 두면 일정 간격으로 아래로 떨어지는 로봇 이미지를 구현하시오.

 

[세부 조건]

  1. 커스텀 뷰 구성: View를 상속받고 Runnable을 구현하는 RobotView 클래스를 만들어 XML 레이아웃에 배치한다. (로봇 이미지 자원: R.drawable.robot)
  2. 초기 위치 설정: 앱이 처음 실행될 때 로봇 이미지는 화면의 정중앙에 위치해야 한다.
  3. 터치 이벤트 (onTouchEvent): 화면을 터치하면, 로봇 이미지의 중심이 사용자가 터치한 좌표(X, Y)로 즉시 이동해야 한다.
  4. 스레드 애니메이션 (run): 별도의 스레드를 실행하여 0.5초(500ms)마다 로봇이 10픽셀씩 아래로 이동하게 만든다.
  5. 경계 처리: 스레드에 의해 로봇이 아래로 떨어질 때, 화면의 가장 아래쪽(바닥)을 벗어나지 않도록 처리한다.

 

▼결과화면

 

▼activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <kr.ac.dju.customviewtest01.RobotView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/RobotView">

        </kr.ac.dju.customviewtest01.RobotView>
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

▼RobotView.java

package kr.ac.dju.customviewtest01;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ResourcesCompat;

import java.util.ResourceBundle;

public class RobotView extends View implements Runnable {

    //멤버추가
    private Drawable image;
    private int viewWidth, viewHeight;
    private int imageWidth, imageHeight;
    private int x, y;

    //생성자
    public RobotView(Context context, @Nullable AttributeSet attrs) {
        super (context, attrs);
        image = ResourcesCompat.getDrawable(getResources(), R.drawable.robot, null);
        Thread thread = new Thread(this);
        thread.start();
    }

    //메소드 추가
    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        image.setBounds(x,y,x+imageWidth,y+imageHeight);
        image.draw(canvas);

    }

    //화면의 크기가 갱신되었을 때 호출되는 메소드
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        viewWidth = this.getWidth(); //전체화면 가로크기
        viewHeight = this.getHeight(); //전체화면 세로

        imageWidth = image.getIntrinsicWidth(); //그림의 가로크기
        imageHeight = image.getIntrinsicHeight(); //그림의 세로크기

        x = viewWidth/2 - imageWidth/2;
        y = viewHeight/2 - imageHeight/2;
    } //end of onSizeChanged

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //1. 클릭시 중앙 정렬
        x = (int)event.getX()-imageWidth/2;
        y = (int)event.getY()-imageHeight/2;

        // 2단계: 경계 클램핑
        //x = Math.max(0, Math.min(x, viewWidth - imageWidth));
        //y =

        invalidate(); //onDraw()호출
        //return super.onTouchEvent(event);
        return true;
    }
    @Override
    public void run(){
        while (true){
            try{
                Thread.sleep(500);
                y = Math.min(viewHeight - imageHeight, y + 10);
                this.postInvalidate();

            } catch  (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}

 

▼MainActivity.java

package kr.ac.dju.customviewtest01;

import android.os.Bundle;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);

    }
}