Android

android 조이스틱

Machine_웅 2022. 3. 25. 14:49
728x90
반응형

JMainActivity

package kr.appgrider.game

import android.graphics.PixelFormat
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import kr.appgrider.JoystickView

class JMainActivity : AppCompatActivity(),  JoystickView.JoystickListener  {

    lateinit var testJoystick : JoystickView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //setContentView(R.layout.activity_jmain)
        //var joystickView = JoystickView(this);
        //setContentView(joystickView)
        setContentView(R.layout.activity_jmain)
        //var joystickView = JoystickView(this);
        // initView()
    }

    // 뷰세팅
    fun initView(){

        testJoystick = findViewById(R.id.testJoystick)
        testJoystick.setZOrderOnTop(true) // 최상단으로 올리고
        testJoystick.holder.setFormat(PixelFormat.TRANSPARENT) // 배경투명하게

    }

    // 조이스틱 이동
    override fun onJoystickMoved(xPercent: Float, yPercent: Float, source: Int) {
        // xPercent 가 +이면 : 오른쪽
        // xPercent 가 -이면 : 왼쪽
        // yPercent 가 +이면 : 아래
        // yPercent 가 -이면 : 위

//        var direction : String ="방향 : ";
//        if(yPercent > 0){
//            direction = direction + "/ 아래 ";
//        }else{
//            direction = direction + "/ 위 ";
//        }
//        if(xPercent > 0){
//            direction = direction + " 오른쪽 ";
//        }else{
//            direction = direction + " 왼쪽 ";
//        }

//        Log.d("Woongs","가로방향 : "+xPercent +" 세로방향 : "+yPercent);
//        Log.d("Woongs","direction : "+direction);
    }
}

 

 

xml

 

package kr.appgrider;

import static android.view.MotionEvent.ACTION_UP;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import androidx.annotation.NonNull;

public class JoystickView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {
    /*
     https://www.instructables.com/A-Simple-Android-UI-Joystick/
    * 1. SurfaceView 상속후  생성자 생성
    * 2. SurfaceHolder.Callback implements 후 함수 implements
    * 3. 생성자에 getHolder().addCallback(this); 이벤트 추가
    * 4. jMain에   var joystickView = JoystickView(this); 인스턴스 생성후
    *    setContentView(R.layout.activity_jmain) 대신에 생성한 인스턴스를 넣는다.
    * 5. 조이스틱 그리기 drawJoystick, setupDimensions 추가 surfaceCreated에 호출
    * 6. View.OnTouchListener 구현
    * 7. 조이스틱 제한하기 (피타고라스 정리) 읭? displacement , interface
    * 8. jMain에 implements 및 값 표현
    * 9. XML 에 JoystickView 추가
    * */

    float centerX;
    float centerY;
    float baseRadius;
    float hatRadius;
    private JoystickListener joystickCallback;
    private final  int ratio = 5;


    public JoystickView(Context context) {
        super(context);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    public JoystickView(Context context, AttributeSet attrs) {
        super(context, attrs);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    public JoystickView(Context context, AttributeSet attrs, int style) {
        super(context, attrs, style);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    // 조이스틱 그리기
    private void drawJoystick(float newX, float newY ){
        if(getHolder().getSurface().isValid()){
            //  해당 캔버스에 이제 그림그리기를 시작하기 위해 lock을 얻어왔다.
            Canvas myCanvas = this.getHolder().lockCanvas();
            Paint colors = new Paint();
            myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // PorterDuff.Mode.CLEAR 빈 공백상태로 만들기

            // 배경원
            colors.setARGB(255,50,50,50);
            myCanvas.drawCircle(centerX,centerY,baseRadius,colors);
            
            // 조이스틱원
            colors.setARGB(255,0,0,250);
            myCanvas.drawCircle(newX,newY,hatRadius,colors);
            getHolder().unlockCanvasAndPost(myCanvas);
        }
    }

    private void setupDimensions(){
        centerX = getWidth() / 2;
        centerY = getHeight() / 2;
        baseRadius = Math.min(getWidth(), getHeight()) / 3;
        hatRadius = Math.min(getWidth(), getHeight()) / 5;
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {


        if(view.equals(this)){ //지금의  SurfaceView에서만 오는 터치이벤트만 허용함
            
            if(motionEvent.getAction() != ACTION_UP){ // 사용자가 스크린에서 손을 떼지 않았다는것을 확인
                float displacement = (float) Math.sqrt(Math.pow(motionEvent.getX() - centerX, 2) + Math.pow(motionEvent.getY() - centerY, 2));
                if(displacement < baseRadius){
                    drawJoystick(motionEvent.getX(),motionEvent.getY());
                    joystickCallback.onJoystickMoved((motionEvent.getX()-centerX) /baseRadius,(motionEvent.getY()-centerY)/baseRadius, getId());
                }else{
                    float ratio = baseRadius/displacement;
                    float constrainX = centerX + (motionEvent.getX() - centerX) * ratio;
                    float constrainY = centerY + (motionEvent.getY() - centerY) * ratio;
                    drawJoystick(constrainX,constrainY);
                    joystickCallback.onJoystickMoved((constrainX-centerX) /baseRadius,(constrainY-centerY)/baseRadius, getId());

                }
            }else{ // 사용자가 손을떼면 조이스틱이 중앙위치로 재배치
                drawJoystick(centerX,centerY);
                joystickCallback.onJoystickMoved(0,0, getId());
            }
        }
        //  false를 반환하면 onTouch 메서드가 향후 터치를 수신하는 것을 방지하므로 true를 반환합니다.
        return true;
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        setupDimensions();
        drawJoystick(centerX,centerY);
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

    }

    public interface JoystickListener
    {
        // 방향 백분률
        // xPercent 가 +이면 : 오른쪽
        // xPercent 가 -이면 : 왼쪽
        // yPercent 가 +이면 : 아래
        // yPercent 가 -이면 : 위

        void onJoystickMoved(float xPercent, float yPercent, int source);
    }
}


 

JoystickView

package kr.appgrider;

import static android.view.MotionEvent.ACTION_UP;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import androidx.annotation.NonNull;

public class JoystickView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {
    /*
     https://www.instructables.com/A-Simple-Android-UI-Joystick/
    * 1. SurfaceView 상속후  생성자 생성
    * 2. SurfaceHolder.Callback implements 후 함수 implements
    * 3. 생성자에 getHolder().addCallback(this); 이벤트 추가
    * 4. jMain에   var joystickView = JoystickView(this); 인스턴스 생성후
    *    setContentView(R.layout.activity_jmain) 대신에 생성한 인스턴스를 넣는다.
    * 5. 조이스틱 그리기 drawJoystick, setupDimensions 추가 surfaceCreated에 호출
    * 6. View.OnTouchListener 구현
    * 7. 조이스틱 제한하기 (피타고라스 정리) 읭? displacement , interface
    * 8. jMain에 implements 및 값 표현
    * 9. XML 에 JoystickView 추가
    * */

    float centerX;
    float centerY;
    float baseRadius;
    float hatRadius;
    private JoystickListener joystickCallback;
    private final  int ratio = 5;


    public JoystickView(Context context) {
        super(context);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    public JoystickView(Context context, AttributeSet attrs) {
        super(context, attrs);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    public JoystickView(Context context, AttributeSet attrs, int style) {
        super(context, attrs, style);
        getHolder().addCallback(this);
        setOnTouchListener(this);

        if(context instanceof JoystickListener){
            joystickCallback = (JoystickListener) context;
            setZOrderOnTop(true);
            getHolder().setFormat(PixelFormat.TRANSPARENT);
        }
    }

    // 조이스틱 그리기
    private void drawJoystick(float newX, float newY ){
        if(getHolder().getSurface().isValid()){
            //  해당 캔버스에 이제 그림그리기를 시작하기 위해 lock을 얻어왔다.
            Canvas myCanvas = this.getHolder().lockCanvas();
            Paint colors = new Paint();
            myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // PorterDuff.Mode.CLEAR 빈 공백상태로 만들기

            // 배경원
            colors.setARGB(255,50,50,50);
            myCanvas.drawCircle(centerX,centerY,baseRadius,colors);
            
            // 조이스틱원
            colors.setARGB(255,0,0,250);
            myCanvas.drawCircle(newX,newY,hatRadius,colors);
            getHolder().unlockCanvasAndPost(myCanvas);
        }
    }

    private void setupDimensions(){
        centerX = getWidth() / 2;
        centerY = getHeight() / 2;
        baseRadius = Math.min(getWidth(), getHeight()) / 3;
        hatRadius = Math.min(getWidth(), getHeight()) / 5;
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {


        if(view.equals(this)){ //지금의  SurfaceView에서만 오는 터치이벤트만 허용함
            
            if(motionEvent.getAction() != ACTION_UP){ // 사용자가 스크린에서 손을 떼지 않았다는것을 확인
                float displacement = (float) Math.sqrt(Math.pow(motionEvent.getX() - centerX, 2) + Math.pow(motionEvent.getY() - centerY, 2));
                if(displacement < baseRadius){
                    drawJoystick(motionEvent.getX(),motionEvent.getY());
                    joystickCallback.onJoystickMoved((motionEvent.getX()-centerX) /baseRadius,(motionEvent.getY()-centerY)/baseRadius, getId());
                }else{
                    float ratio = baseRadius/displacement;
                    float constrainX = centerX + (motionEvent.getX() - centerX) * ratio;
                    float constrainY = centerY + (motionEvent.getY() - centerY) * ratio;
                    drawJoystick(constrainX,constrainY);
                    joystickCallback.onJoystickMoved((constrainX-centerX) /baseRadius,(constrainY-centerY)/baseRadius, getId());

                }
            }else{ // 사용자가 손을떼면 조이스틱이 중앙위치로 재배치
                drawJoystick(centerX,centerY);
                joystickCallback.onJoystickMoved(0,0, getId());
            }
        }
        //  false를 반환하면 onTouch 메서드가 향후 터치를 수신하는 것을 방지하므로 true를 반환합니다.
        return true;
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        setupDimensions();
        drawJoystick(centerX,centerY);
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {

    }

    public interface JoystickListener
    {
        // 방향 백분률
        // xPercent 가 +이면 : 오른쪽
        // xPercent 가 -이면 : 왼쪽
        // yPercent 가 +이면 : 아래
        // yPercent 가 -이면 : 위

        void onJoystickMoved(float xPercent, float yPercent, int source);
    }
}


728x90
반응형

'Android' 카테고리의 다른 글

Android Gradle ?? - Android 빌드시스템  (0) 2022.09.17
Android UI 파편화 관련  (0) 2022.09.01
(스크랩 ) android animation 종류  (0) 2022.03.02
AAC : navigation  (0) 2022.02.21
android ISP 연동 관련  (0) 2022.01.25