다음을 통해 공유


연습 - Android에서 Touch 사용

작업 중인 애플리케이션에서 이전 섹션의 개념을 사용하는 방법을 살펴보겠습니다. 네 개의 활동이 있는 애플리케이션을 만듭니다. 첫 번째 작업은 다양한 API를 보여 주는 다른 활동을 시작하는 메뉴 또는 스위치보드입니다. 다음 스크린샷은 기본 활동을 보여줍니다.

Touch Me 단추가 있는 예제 스크린샷

첫 번째 작업인 터치 샘플은 이벤트 처리기를 사용하여 뷰를 터치하는 방법을 보여 줍니다. 제스처 인식기 활동은 이벤트를 서브클래스 Android.View.Views 하고 처리하는 방법뿐만 아니라 손가락 모으기 제스처를 처리하는 방법을 보여 줍니다. 세 번째이자 마지막 작업인 사용자 지정 제스처는 사용자 지정 제스처를 사용하는 방법을 보여 줍니다. 작업을 더 쉽게 따르고 흡수할 수 있도록 이 연습을 섹션으로 나누어 각 섹션이 활동 중 하나에 초점을 맞춥니다.

터치 샘플 작업

  • 프로젝트 TouchWalkthrough_Start 엽니다. MainActivity는 모두 이동하도록 설정되어 있습니다. 활동에서 터치 동작을 구현하는 것은 우리에게 달려 있습니다. 애플리케이션을 실행하고 터치 샘플을 클릭하면 다음 작업이 시작됩니다.

    Touch Begins가 표시된 활동 스크린샷

  • 이제 작업이 시작되는지 확인했으므로 파일 TouchActivity.cs 열고 다음 이벤트에 ImageView대한 Touch 처리기를 추가합니다.

    _touchMeImageView.Touch += TouchMeImageViewOnTouch;
    
  • 다음으로 다음 메서드를 추가하여 TouchActivity.cs.

    private void TouchMeImageViewOnTouch(object sender, View.TouchEventArgs touchEventArgs)
    {
        string message;
        switch (touchEventArgs.Event.Action & MotionEventActions.Mask)
        {
            case MotionEventActions.Down:
            case MotionEventActions.Move:
            message = "Touch Begins";
            break;
    
            case MotionEventActions.Up:
            message = "Touch Ends";
            break;
    
            default:
            message = string.Empty;
            break;
        }
    
        _touchInfoTextView.Text = message;
    }
    

위의 코드에서 작업과 Down 동작을 Move 동일하게 처리합니다. 사용자가 손가락을 떼 ImageView지 않더라도 이동하거나 사용자가 가하는 압력이 변경될 수 있기 때문입니다. 이러한 유형의 변경은 작업을 생성합니다 Move .

사용자가 터치 ImageViewTouch 할 때마다 이벤트가 발생하고 처리기가 다음 스크린샷과 같이 화면에 Touch Begins 메시지를 표시합니다.

Touch Begins를 사용하는 활동의 스크린샷

사용자가 터치ImageView하는 한 Touch Begins가 에 표시됩니다TextView. 사용자가 더 이상 터치ImageView하지 않으면 다음 스크린샷과 TextView같이 Touch Ends 메시지가 표시됩니다.

터치 엔드가 있는 활동의 스크린샷

제스처 인식기 활동

이제 제스처 인식기 작업을 구현할 수 있습니다. 이 활동은 화면 주위에 보기를 끌어서 확대/축소를 구현하는 한 가지 방법을 보여 줍니다.

  • 라는 GestureRecognizer애플리케이션에 새 활동을 추가합니다. 다음 코드와 유사하게 이 작업에 대한 코드를 편집합니다.

    public class GestureRecognizerActivity : Activity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            View v = new GestureRecognizerView(this);
            SetContentView(v);
        }
    }
    
  • 프로젝트에 새 Android 보기를 추가하고 이름을 지정합니다 GestureRecognizerView. 이 클래스에 다음 변수를 추가합니다.

    private static readonly int InvalidPointerId = -1;
    
    private readonly Drawable _icon;
    private readonly ScaleGestureDetector _scaleDetector;
    
    private int _activePointerId = InvalidPointerId;
    private float _lastTouchX;
    private float _lastTouchY;
    private float _posX;
    private float _posY;
    private float _scaleFactor = 1.0f;
    
  • 에 다음 생성자를 추가합니다 GestureRecognizerView. 이 생성자는 활동에 추가 ImageView 됩니다. 이 시점에서 코드는 여전히 컴파일되지 않습니다. 사용자가 꼬집을 때 크기를 조정하는 ImageView 데 도움이 되는 클래스 MyScaleListener 를 만들어야 합니다.

    public GestureRecognizerView(Context context): base(context, null, 0)
    {
        _icon = context.Resources.GetDrawable(Resource.Drawable.Icon);
        _icon.SetBounds(0, 0, _icon.IntrinsicWidth, _icon.IntrinsicHeight);
        _scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this));
    }
    
  • 작업에서 이미지를 그리려면 다음 코드 조각과 같이 View 클래스의 메서드를 재정 OnDraw 의해야 합니다. 이 코드는 지정된 _posX_posY 위치로 이동하고 ImageView 배율 인수에 따라 이미지의 크기를 조정합니다.

    protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);
        canvas.Save();
        canvas.Translate(_posX, _posY);
        canvas.Scale(_scaleFactor, _scaleFactor);
        _icon.Draw(canvas);
        canvas.Restore();
    }
    
  • 다음으로 사용자가 을 꼬집을 때 인스턴스 변수 _scaleFactor 를 업데이트해야 합니다 ImageView. 라는 MyScaleListener클래스를 추가합니다. 이 클래스는 사용자가 꼬집을 때 Android에서 발생하는 크기 조정 이벤트를 수신 대기합니다 ImageView. 에 다음 내부 클래스를 추가합니다 GestureRecognizerView. 이 클래스는 .입니다 ScaleGesture.SimpleOnScaleGestureListener. 이 클래스는 제스처의 하위 집합에 관심이 있을 때 수신기가 서브클래스할 수 있는 편리한 클래스입니다.

    private class MyScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
    {
        private readonly GestureRecognizerView _view;
    
        public MyScaleListener(GestureRecognizerView view)
        {
            _view = view;
        }
    
        public override bool OnScale(ScaleGestureDetector detector)
        {
            _view._scaleFactor *= detector.ScaleFactor;
    
            // put a limit on how small or big the image can get.
            if (_view._scaleFactor > 5.0f)
            {
                _view._scaleFactor = 5.0f;
            }
            if (_view._scaleFactor < 0.1f)
            {
                _view._scaleFactor = 0.1f;
            }
    
            _view.Invalidate();
            return true;
        }
    }
    
  • 재정 GestureRecognizerView 의해야 하는 다음 방법은 .입니다 OnTouchEvent. 다음 코드는 이 메서드의 전체 구현을 나열합니다. 여기에 많은 코드가 있으므로 잠시 시간을 내어 여기서 무슨 일이 일어나고 있는지 살펴보겠습니다. 이 메서드가 가장 먼저 수행하는 작업은 필요한 경우 아이콘 크기를 조정하는 것입니다. 이 작업은 호출 _scaleDetector.OnTouchEvent을 통해 처리됩니다. 다음으로 이 메서드를 호출한 작업을 알아봅니다.

    • 사용자가 화면을 터치한 경우 X 및 Y 위치와 화면을 터치한 첫 번째 포인터의 ID를 기록합니다.

    • 사용자가 화면에서 터치를 이동한 경우 사용자가 포인터를 이동한 정도를 파악합니다.

    • 사용자가 화면에서 손가락을 떼면 제스처 추적을 중지합니다.

    public override bool OnTouchEvent(MotionEvent ev)
    {
        _scaleDetector.OnTouchEvent(ev);
    
        MotionEventActions action = ev.Action & MotionEventActions.Mask;
        int pointerIndex;
    
        switch (action)
        {
            case MotionEventActions.Down:
            _lastTouchX = ev.GetX();
            _lastTouchY = ev.GetY();
            _activePointerId = ev.GetPointerId(0);
            break;
    
            case MotionEventActions.Move:
            pointerIndex = ev.FindPointerIndex(_activePointerId);
            float x = ev.GetX(pointerIndex);
            float y = ev.GetY(pointerIndex);
            if (!_scaleDetector.IsInProgress)
            {
                // Only move the ScaleGestureDetector isn't already processing a gesture.
                float deltaX = x - _lastTouchX;
                float deltaY = y - _lastTouchY;
                _posX += deltaX;
                _posY += deltaY;
                Invalidate();
            }
    
            _lastTouchX = x;
            _lastTouchY = y;
            break;
    
            case MotionEventActions.Up:
            case MotionEventActions.Cancel:
            // We no longer need to keep track of the active pointer.
            _activePointerId = InvalidPointerId;
            break;
    
            case MotionEventActions.PointerUp:
            // check to make sure that the pointer that went up is for the gesture we're tracking.
            pointerIndex = (int) (ev.Action & MotionEventActions.PointerIndexMask) >> (int) MotionEventActions.PointerIndexShift;
            int pointerId = ev.GetPointerId(pointerIndex);
            if (pointerId == _activePointerId)
            {
                // This was our active pointer going up. Choose a new
                // action pointer and adjust accordingly
                int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                _lastTouchX = ev.GetX(newPointerIndex);
                _lastTouchY = ev.GetY(newPointerIndex);
                _activePointerId = ev.GetPointerId(newPointerIndex);
            }
            break;
    
        }
        return true;
    }
    
  • 이제 애플리케이션을 실행하고 Gesture Recognizer 작업을 시작합니다. 화면이 시작되면 아래 스크린샷과 같이 표시됩니다.

    Android 아이콘이 있는 제스처 인식기 시작 화면

  • 이제 아이콘을 터치하고 화면 주위로 끕니다. 손가락 모으기에서 확대/축소 제스처를 시도합니다. 어떤 시점에서 화면은 다음 스크린샷처럼 보일 수 있습니다.

    화면 주위의 제스처 이동 아이콘

이 시점에서 당신은 자신에게 뒷면에 팻을 제공해야합니다 : 당신은 안드로이드 애플리케이션에서 손가락 모으기 - 투 - 줌을 구현했습니다! 잠시 휴식을 취하고 사용자 지정 제스처를 사용하여 이 연습에서 세 번째이자 마지막 작업으로 이동할 수 있습니다.

사용자 지정 제스처 작업

이 연습의 마지막 화면에서는 사용자 지정 제스처를 사용합니다.

이 연습을 위해 제스처 라이브러리는 제스처 도구를 사용하여 이미 만들어졌으며 리소스/원시/제스처 파일 의 프로젝트에 추가되었습니다. 이 약간의 하우스키핑을 통해 연습에서 최종 활동을 시작해 보세요.

  • 다음 내용을 사용하여 프로젝트에 custom_gesture_layout.axml이라는 레이아웃 파일을 추가합니다. 프로젝트에는 Resources 폴더에 이미 모든 이미지가 있습니다.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
        <ImageView
            android:src="@drawable/check_me"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="3"
            android:id="@+id/imageView1"
            android:layout_gravity="center_vertical" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    </LinearLayout>
    
  • 그런 다음 프로젝트에 새 활동을 추가하고 이름을 지정합니다 CustomGestureRecognizerActivity.cs. 다음 두 코드 줄에 표시된 것처럼 클래스에 두 개의 인스턴스 변수를 추가합니다.

    private GestureLibrary _gestureLibrary;
    private ImageView _imageView;
    
  • OnCreate 다음 코드와 유사하게 이 작업의 메서드를 편집합니다. 이 코드에서 진행 중인 작업을 설명하는 데 1분 정도 걸릴 수 있습니다. 가장 먼저 수행할 작업은 활동의 루트 뷰로 인스턴스화 GestureOverlayView 하고 설정하는 것입니다. 또한 이벤트 처리기를 GesturePerformed 이벤트에 할당합니다 GestureOverlayView. 다음으로 이전에 만든 레이아웃 파일을 확장한 다음 이를 자식 뷰 GestureOverlayView로 추가합니다. 마지막 단계는 변수 _gestureLibrary 를 초기화하고 애플리케이션 리소스에서 제스처 파일을 로드하는 것입니다. 어떤 이유로 제스처 파일을 로드할 수 없는 경우 이 작업이 수행할 수 있는 작업이 많지 않으므로 종료됩니다.

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
    
        GestureOverlayView gestureOverlayView = new GestureOverlayView(this);
        SetContentView(gestureOverlayView);
        gestureOverlayView.GesturePerformed += GestureOverlayViewOnGesturePerformed;
    
        View view = LayoutInflater.Inflate(Resource.Layout.custom_gesture_layout, null);
        _imageView = view.FindViewById<ImageView>(Resource.Id.imageView1);
        gestureOverlayView.AddView(view);
    
        _gestureLibrary = GestureLibraries.FromRawResource(this, Resource.Raw.gestures);
        if (!_gestureLibrary.Load())
        {
            Log.Wtf(GetType().FullName, "There was a problem loading the gesture library.");
            Finish();
        }
    }
    
  • 다음 코드 조각과 같이 메서드 GestureOverlayViewOnGesturePerformed 를 구현해야 하는 마지막 작업입니다. 제스처를 GestureOverlayView 감지하면 이 메서드를 다시 호출합니다. 먼저 제스처와 일치하는 개체를 IList<Prediction> 호출 _gestureLibrary.Recognize()하여 가져옵니다. 약간의 LINQ를 사용하여 제스처에 Prediction 대한 점수가 가장 높은 것을 가져옵니다.

    점수가 충분히 높은 일치하는 제스처가 없으면 이벤트 처리기는 아무 작업도 수행하지 않고 종료됩니다. 그렇지 않으면 예측 이름을 검사 제스처의 이름에 따라 표시되는 이미지를 변경합니다.

    private void GestureOverlayViewOnGesturePerformed(object sender, GestureOverlayView.GesturePerformedEventArgs gesturePerformedEventArgs)
    {
        IEnumerable<Prediction> predictions = from p in _gestureLibrary.Recognize(gesturePerformedEventArgs.Gesture)
        orderby p.Score descending
        where p.Score > 1.0
        select p;
        Prediction prediction = predictions.FirstOrDefault();
    
        if (prediction == null)
        {
            Log.Debug(GetType().FullName, "Nothing seemed to match the user's gesture, so don't do anything.");
            return;
        }
    
        Log.Debug(GetType().FullName, "Using the prediction named {0} with a score of {1}.", prediction.Name, prediction.Score);
    
        if (prediction.Name.StartsWith("checkmark"))
        {
            _imageView.SetImageResource(Resource.Drawable.checked_me);
        }
        else if (prediction.Name.StartsWith("erase", StringComparison.OrdinalIgnoreCase))
        {
            // Match one of our "erase" gestures
            _imageView.SetImageResource(Resource.Drawable.check_me);
        }
    }
    
  • 애플리케이션을 실행하고 사용자 지정 제스처 인식기 작업을 시작합니다. 다음 스크린샷과 같이 표시됩니다.

    Check Me 이미지가 있는 스크린샷

    이제 화면에 검사 표시를 그리고 표시되는 비트맵은 다음 스크린샷에 표시된 것과 같이 표시됩니다.

    검사mark, 검사mark가 인식됨

    마지막으로 화면에 낙서를 그립니다. 검사 상자는 다음 스크린샷에 표시된 대로 원래 이미지로 다시 변경되어야 합니다.

    화면에 낙서, 원래 이미지가 표시됩니다.

이제 Xamarin.Android를 사용하여 Android 애플리케이션에서 터치 및 제스처를 통합하는 방법을 이해했습니다.