줌 이후 이미지 이동 Move 사용시에..

namespace Project { [Activity(Label = "Project", MainLauncher = true)] public class Main_Activity : Activity, View.IOnTouchListener { LinearLayout Main_Contact_Layout; FrameLayout _Main_Frame; ImageView _Main_Background; private List _DrawerListItem; private ListView _DrawerList;

    float _X, _Y;
    int offset = 0, duration = 100;
    float scaleX = 1.0f, scaleY = 1.0f;
    float maxZoomLimit = 2.6f, minZoomLimit = 1.0f;


    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        RequestWindowFeature(WindowFeatures.NoTitle);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main_Layout);

        _Main_Contact_Layout = (LinearLayout)FindViewById(Resource.Id._Main_Content_Layout);
        _Main_Contact_Layout.SetOnTouchListener(this);

        _Main_Frame = (FrameLayout)FindViewById(Resource.Id._Main_Frame);

        _Main_Background = (ImageView)FindViewById(Resource.Id._Main_Background);




        Button _Contact_Menu_Button = (Button)FindViewById(Resource.Id._Contact_Menu_Button);

        _Contact_Menu_Button.Click += (s, e) =>
        {
            Intent _Sub_Main_Layout = new Intent(this, typeof(Sub_Main_Activity));
            this.StartActivity(_Sub_Main_Layout);
        };



        Button _Contact_Up_Button = (Button)FindViewById(Resource.Id._Contact_Up_Button);
        Button _Contact_Down_Button = (Button)FindViewById(Resource.Id._Contact_Down_Button);

        _Contact_Up_Button.Click += (s, e) =>
        {
            _Zoom_In(_Main_Background);
        };

        _Contact_Down_Button.Click += (s, e) =>
        {
            _Zoom_Out(_Main_Background);
        };



        Button _Contant_Power_Button = (Button)FindViewById(Resource.Id._Contact_Power_Button);

        _Contant_Power_Button.LongClick += (s, e) =>
        {
            LayoutInflater inflater = (LayoutInflater)this.GetSystemService(Context.LayoutInflaterService);
            View _Power_Popup = inflater.Inflate(Resource.Layout.Custom_Power_Popup, null);

            PopupWindow _Power = new PopupWindow(_Power_Popup, 500, 300);
            _Power.SetBackgroundDrawable(new BitmapDrawable());
            //_Power.OutsideTouchable = true;

            ImageButton _Custom_Back_Button = (ImageButton)_Power_Popup.FindViewById(Resource.Id._Custom_Back_Button);
            ImageButton _Custom_Exit_Button = (ImageButton)_Power_Popup.FindViewById(Resource.Id._Custom_Exit_Button);

            //_Power.ShowAsDropDown(_Power_Popup, _Params.X, _Params.Y);
            _Power.ShowAtLocation(_Power_Popup, GravityFlags.Center, 0, 0);

            _Custom_Back_Button.Click += (a, b) =>
            {
                _Power.Dismiss();
            };

            _Custom_Exit_Button.Click += (u, n) =>
            {
                Finish();
            };

            _Contant_Power_Button.Click += (c, d) =>
            {
                if (_Power.IsShowing)
                {
                    Finish();
                }
            };
        };


        _Contant_Power_Button.Click += (s, e) =>
        {

        };




        _DrawerList = FindViewById<ListView>(Resource.Id._DrawerList);
        SetDrawerList();
        ArrayAdapter<string> _DrawerAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, _DrawerList_Item);
        _DrawerList.Adapter = _DrawerAdapter;

        _DrawerList.ItemClick += _DrawerList_ItemClick;

    }




    public bool OnTouch(View v, MotionEvent e)
    {
        switch (e.Action)
        {
            case MotionEventActions.Down:

                _X = e.GetX();
                _Y = e.GetY();

                float _Start_X = 760, _Start_Y = 160;
                float _End_X = 1150, _End_Y = 490;

                WindowManagerLayoutParams _Params = new WindowManagerLayoutParams();
                _Params.X = (int)e.RawX;
                _Params.Y = (int)e.RawY;

                LayoutInflater inflater = (LayoutInflater)this.GetSystemService(Context.LayoutInflaterService);
                View _Popup = inflater.Inflate(Resource.Layout.Custom_Popup, null);

                PopupWindow _Window = new PopupWindow(_Popup, WindowManagerLayoutParams.WrapContent, WindowManagerLayoutParams.WrapContent);
                _Window.SetBackgroundDrawable(new BitmapDrawable());
                _Window.OutsideTouchable = true;

                TextView _SetupX = (TextView)_Popup.FindViewById(Resource.Id.x_text);
                TextView _SetupY = (TextView)_Popup.FindViewById(Resource.Id.y_text);

                _SetupX.Text = "X 좌표 : " + _X.ToString("#.##");
                _SetupY.Text = "Y 좌표 : " + _Y.ToString("#.##");

                DrawerLayout _lstDrawer = (DrawerLayout)v.FindViewById(Resource.Id._DrawerLayout);


                if(_Start_X > _X | _Start_Y > _Y | _End_X < _X | _End_Y < _Y )
                {
                    _Window.Focusable = true;
                    _Window.ShowAsDropDown(_Popup, _Params.X, _Params.Y);
                    _Window.ShowAtLocation(_Popup, GravityFlags.Left | GravityFlags.Top, 0, 0);
                    _lstDrawer.CloseDrawer((int)GravityFlags.Left);
                }

                else if (_Start_X < _X & _Start_Y < _Y & _End_X > _X & _End_Y > _Y & !_lstDrawer.IsDrawerOpen((int)GravityFlags.Left))
                {
                    _lstDrawer.OpenDrawer((int)GravityFlags.Left);
                    _Window.ShowAsDropDown(_Popup, _Params.X, _Params.Y);
                    _Window.ShowAtLocation(_Popup, GravityFlags.Left | GravityFlags.Top, 0, 0);
                }

                else if (_lstDrawer.IsDrawerOpen((int)GravityFlags.Left))
                {
                    _lstDrawer.CloseDrawer((int)GravityFlags.Left);
                }

                break;


            case MotionEventActions.Move:

                int w = _Main_Background.MaxWidth;
                int h = _Main_Background.MaxHeight;

                float x = _Main_Background.GetX();
                float y = _Main_Background.GetY();

                int s = _Main_Contact_Layout.Width;
                int m = _Main_Contact_Layout.Height;

                float xx = e.RawX - _X;
                float yy = e.RawY - _Y;

                if (scaleX > 1.0f & scaleY > 1.0f & xx < w & yy < h)
                {
                    _Main_Background.SetX(xx);
                    _Main_Background.SetY(yy);
                }

                break;
        }

        return true;
    }


    private void _DrawerList_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
    {
        if (_DrawerList_Item[e.Position] == "One")
        {
            Toast.MakeText(this, "첫번째 클릭 이벤트", ToastLength.Short).Show();
        }
    }



    private void SetDrawerList()
    {
        _DrawerList_Item = new List<string>();
        _DrawerList_Item.Add("One");
        _DrawerList_Item.Add("Two");
        _DrawerList_Item.Add("Three");
        _DrawerList_Item.Add("Four");
        _DrawerList_Item.Add("Five");
    }


    private void _Zoom_In(View v)
    {
        if (scaleX < maxZoomLimit && scaleY < maxZoomLimit)
        {
            Animation animation = new ScaleAnimation(scaleX, (scaleX + 0.2f), scaleY, (scaleY + 0.2f), _X, _Y);
            scaleX += 0.2f;
            scaleY += 0.2f;
            animation.Interpolator = new DecelerateInterpolator();
            animation.Duration = duration;
            animation.StartOffset = offset;
            animation.FillAfter = true;
            v.StartAnimation(animation);
        }
    }

    private void _Zoom_Out(View v)
    {
        if (scaleX > minZoomLimit && scaleY > minZoomLimit)
        {
            Animation animation = new ScaleAnimation(scaleX, (scaleX - 0.2f), scaleY, (scaleY - 0.2f), _X, _Y);
            scaleY -= 0.2f;
            scaleX -= 0.2f;
            animation.Interpolator = new DecelerateInterpolator();
            animation.Duration = duration;
            animation.StartOffset = offset;
            animation.FillAfter = true;
            v.StartAnimation(animation);
        }
    }
}

}

위에가 현재 코딩 내용입니다. 버튼을 이용해서 Zoom in, Zoom out을 하고 그 상태에서 화면을 드래그하면 이미지든 레이아웃이든 이동을 하고 싶은데

지금 문제는

화면이 제가 터치한 부분부터 이동이 되지 않고 자꾸 한점으로 이동한다는 겁니다 이 부분에서 대해서는 어디가 문제인지는 알겠는데 어떻게 바꿔야할지... 위 Down 부분에서 터치한 부분의 좌표를 저장해서 그걸 불러왓는데도 불구하고 안되더라구요..

그리고 또 하나의 문제는 자꾸 화면이 밖으로 나갑니다. 이미지 끝을 기준으로 바꾸로 나가지 않도록 여러가지 시도를 해봣는데도 불구하고 계속 밖으로 나가버립니다..

어떻게해야할까요?ㅠ

위에 코드는 Xamarin으로 되어있지만 안드로이드 스튜디오 자바용으로 답을 내주셔도 괜찮습니다. 무엇이든 일단 도와주시면 너무너무 감사드리겠습니다 ㅠ

2답변

  • 좋아요

    1

    싫어요
    채택취소하기

    몇가지 도움이 되실만한 가이드를 해드리겠습니다.

    1. ACTION_DOWN 시 touch x,y 좌표를 받고 ACTION_MOVE 에서도 ImageView 의 x,y 위치 이동 후 touch x,y 좌표를 받습니다. 이때 ImageView 의 x,y 는 MOVE 이벤트 시 x,y 에서 저장 된 touch x,y 의 좌표를 뺀 delta 값을 ImageView.x += dx ImageView.y += dy 식으로 세팅 합니다

    대략적으로 아래와 같은 형태가 되겠네요.

    if(event.action == MotionEvent.ACTION_DOWN) {
        touchX = event.x
        touchY = event.y
    }else if(event.action == MotionEvent.ACTION_MOVE){
        dx = event.x - touchX
        dy = event.y - touchY
    
        ImageView.x += dx
        ImageView.y += dy
    
        touchX = event.x
        touchY = event.y
    }
    
    
    1. 추가로 해야 될 작업은 x,y 의 최소값 최대값을 설정하여 이동시키는 것인데, if(xx < w & yy < h) 로 조건문을 설정하게 되면 x 는 이동가능한데 y 조건때문에 이동이 안되거나 반대로 y는 이동가능한데 x 조건때문에 이동이 안되는 경우가 생기므로 x,y 를 && 가 아닌 || 로 각각 처리 하여야 할듯 싶네요. 아래와 같이 처리해볼수 있을 것 같습니다. (동작 확인을 해본것은 아니기 때문에 참고만 해주세요)
    if (scaleX > 1.0f & scaleY > 1.0f)
    {
            moveToX(dx)
            moveToY(dy)
    }
    
    fun moveToX(dx : Int){
            val futureX = ImageView.x + dx
            if(futureX 가 최소 ~ 최대 범위 안에 속할 경우) ImageView.x = futureX
    }
    
    fun moveToY(dy: Int){
            val futureY = ImageView.y + dy
            if(futureY 가 최소 ~ 최대 범위 안에 속할 경우) ImageView.y = futureY
    }
    
    1. 이제 설정하려는 x,y 가 최소~최대 범위 안에 속하는지 체크하는 부분인데, 유의하실점은 scale animation 적용 시 실제 view 의 x,y position 이 scale 된 만큼 갱신 되는것이 아니기 때문에 최초 ImageView 의 x,y 기준에서 scale x,y 값에 따라 계산을 해서 최소~최대범위를 설정해야 하는 점입니다.

    2. 마지막으로 어차피 줌 버튼이 별도로 있는 것이라면 ScaleAnimation pivot 매개변수에 x,y 좌표를 직접 세팅하신것은 크게 의미가 없는 것 같고 RELATIVE_TO_SELF 를사용하시는게 좋을 것 같습니다.

    Animation animation = new ScaleAnimation(
            scaleX, 
            (scaleX + 0.2f), 
            scaleY, 
            (scaleY + 0.2f), 
            Animation.RELATIVE_TO_SELF, 0.5f,  
            Animation.RELATIVE_TO_SELF, 0.5f);
    
    Animation animation = new ScaleAnimation(
            scaleX, 
            (scaleX - 0.2f), 
            scaleY, 
            (scaleY - 0.2f), 
            Animation.RELATIVE_TO_SELF, 0.5f,  
            Animation.RELATIVE_TO_SELF, 0.5f);
    
    • 정말 감사합니다. SiRoNYuHa 2018.6.11 13:57
    • 몇가지 질문이 있는데 .. 괜찮을까요? SiRoNYuHa 2018.6.11 13:57
    • 넵 :) pistolcaffe 2018.6.11 14:42
    • 너무 기본적인 질문 같아서 죄송하고 부끄럽지만, Move 부분에서 ImageView 제 코드에서는 _Main_Backgroud 라는 이름이죠, 이 부분에 x좌표에 dx의 내용을 저장시킬 때, _Main_Background.x += dx 로 했을 때는 View.X 맴버는 인스턴스 참조를 할 수 없다고 하고, 그렇다고 GetX로 했을 때는 매서드 그룹에는 할당할 수 없다고 하는데, 어떻게 해야하나요.. SiRoNYuHa 2018.6.11 15:48
    • kotlin 의 경우 필드에 기본 접근자 설정자 메소드를 생략하고 사용 할 수 있기 때문에 저렇게 사용 한 것이구요. View.setX(View.getX() + dx) 로 처리하면 될것 같습니다. pistolcaffe 2018.6.11 15:52
    • 하나만 더 질문을... 정말 죄송합니다.. SiRoNYuHa 2018.6.11 16:06
    • fun moveToX(dx : Int){ val futureX = ImageView.x + dx if(futureX 가 최소 ~ 최대 범위 안에 속할 경우) ImageView.x = futureX } 이 부분에 대해서인데요, 말씀하신거에 따르면 Kotlin에서 사용하셔서 저런식으로 나온거겠죠? 제가 kotlin에 대해서는 아는게 거의 없다 싶어서 그런데 저 부분은 어떤식으로 바꿔야할지.. 감이 잘 잡히질 않네요ㅠ 정말 정말 죄송합니다... SiRoNYuHa 2018.6.11 16:08
    • private void _MoveToX(float _XX) { float futureX = _Main_Background.GetX() + _XX; if (futureX > 0 | futureX < 500) { _Main_Background.SetX(_Main_Background.GetX()) = futureX; } } 이런식으로 바꾸어는 봣는데, 잘못된 부분이 있을까요? 많은것 같아보이지만.. 에러가 발생하는 부분은 역시나 _Main_Background.SetX(_Main_Background.GetX()) = futureX; 이 부분입니다. _Main_Background.GetX() = futureX; 식으로해도 에러는 발생하더라구요. ㅠ SiRoNYuHa 2018.6.11 16:12
    • 네 저도 Xamarin 은 써본적이 없어서 주로쓰고있는 kotlin 으로 설명을 드렸어요. 일단 맥락만 보시면 futureX 라는 지역변수에 dx 에 따른 이동 position 을 저장해두고, 화면에 벗어나지 않는 범위로 if 문을 구성해서 View.setX(futureX) 로 하시면 될 것 같습니다. 만약 scale 기능이 없다면 if(futuerX >=0 && futureX <= (contentLayout.width - imageView.width) 가 되겠지만 위에 언급한대로 scale 적용 시 해당 view 의 x,y 도 scale 되는 것이 아니기 때문에 아마도 최초 view 의 x,y 기준으로 scale x,y 를 혼합하여 그에따른 보정값을 생각해서 조건문을 작성하셔야 할 것 같습니다. pistolcaffe 2018.6.11 16:15
    • getX() 는 값을 가져오는 것이기 때문에 바로 할당은 안되구요. setX(futuerX) 식으로 사용하셔야 할것 같습니다. pistolcaffe 2018.6.11 16:16
    • 결국 저는 똑같은 질문을 두 번이나 하게 된거군요, 일단 SetX라는 거의 대한 정확한 이해가 되었습니다! 정말 감사합니다. 실행해본 결과로는 흠, 누르는 순간 이미지가 사라져버리네요 ㅎ 어디가 잘못된건지 한번 파악해보고 오겠습니다 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ SiRoNYuHa 2018.6.11 16:20
    • 로그 찍어보시면서 어느 부분에서 xy 포지션이 잘못된건지 체크해보시면 도움이 되실듯 합니다 ㅋㅋ pistolcaffe 2018.6.11 16:29
    • 로그를 찍어보니까 Move 하는 순간 dx, dy 의 값이 0으로 바뀌고 변화가 없네요 왜지... SiRoNYuHa 2018.6.11 16:34
    • move 이벤트 에서 현재 터치 x,y 값에서 기존 touch x,y 값을 뺀 것이 dx,dy 인데 0에서 변화가 없는건 좀 이상하네요.. pistolcaffe 2018.6.11 16:50
    • 좀 틀린 부분 수정해서 이제 정상적으로 터치한 부분부터 움직이는건 되는것 같아요 근데 바깥으로 나가는건 아직수정이 안되네요 ㅠ SiRoNYuHa 2018.6.11 17:22
    • 아무래도 futureY 가 최소 ~ 최대 범위 안에 속할 경우 이부분에서 뭔가 틀린 것 같은데 이미지뷰에 Height 최대값보다 아래로 하고 0보다 크게 했는데도 안되는거보니 좀 생각을 해야겠네요 ㅠ SiRoNYuHa 2018.6.11 17:25
    • 네 그렇게 하면 아마 scaleX,Y 비율에 따라 화면에서 벗어나실거에요. 위에서 언급했던것 처럼 scale 시키면 x,y 값도 scale 이 되는게 아니기 떄문에 scale 비율에 따른 보정값을 추가해야 될것 같아보이네요 pistolcaffe 2018.6.11 17:43
    • 그럼 Scale를 쓰지 않고 확대 축소하는 방법도 있는건가요? 보정이라... 흠.. SiRoNYuHa 2018.6.11 17:45
    • ScaleGestureDetector 란 것도 있는데 원하시는 기능 구현하는데 적합한 API 인지 까지는 모르겠네요 ㅜ pistolcaffe 2018.6.11 18:05
    • 아하 그럼 지금 가장 해볼만한 방법인 Scale의 보정을 해보는거군요... 또 한번 인터넷 검색에 늪에.... 그래도 1주일하고 3일 고민하던걸 반정도 해결해주셔서 감사합니다 ㅠ 정말 너무너무 감사합니다... 보정하는데 또 몇일 걸릴지는 모르겠지만 ㅎㅎ SiRoNYuHa 2018.6.11 18:06
    • 넵 scale 을 시켜도 width, height 값 자체를 키워버리는건 아니기 때문에 scale 하였을때 scale 비율을 가지고 가상의 width , height 를 계산해서 하면 되지 않을까 싶어요 ㅋㅋ pistolcaffe 2018.6.11 18:39
    • 뭔가 굉장히 복잡하네요... 할 수 있을지... ㅠ SiRoNYuHa 2018.6.11 19:41
    • 혹시 괜찮으시면 방법을 좀 알려주실 수 있나요 ㅠㅠ 삼일동안 고민해봣는데 진짜 이런저런 방법 다 써봐도 해결이 되질 않아요 ㅠ SiRoNYuHa 2018.6.14 09:56
    • 일단 어떤 조언이든 상관은 없습니다. 조금이라도 힌트가 될 수 잇다면 ㅠ SiRoNYuHa 2018.6.14 10:53
    • 예제 코드 올려봤는데 참고 해보세요. pistolcaffe 2018.6.14 11:56
    • 감사합니다! 도전해보겠습니다! SiRoNYuHa 2018.6.14 11:59
  • 예시 입니다. kotlin 언어로 작성하였으니 참고 하시고 개발하시는 언어로 바꿔서 사용해 보세요. animation 부분도 좀 변경하였습니다.

    private fun allowX(deltaX: Float) {
        val futureX = (img.x) + deltaX
        val delta = (img.width * scaleX - img.width) / 2
        if (futureX in (0 + delta)..(frame.width - img.width - delta)) img.x = futureX
    }
    
    private fun allowY(deltaY: Float) {
        val futureY = (img.y) + deltaY
        val delta = (img.height * scaleY - img.height) / 2
        if (futureY in (0 + delta)..(frame.height - img.height - delta)) img.y = futureY
    }
    
    private fun zoomIn() {
        if (scaleX < maxZoomLimit && scaleY < maxZoomLimit) {
            scaleX += 0.2f
            scaleY += 0.2f
            img.animate().scaleX(scaleX).scaleY(scaleY).duration = duration.toLong()
        }
    }
    
    private fun zoomOut() {
        if (scaleX > minZoomLimit && scaleY > minZoomLimit) {
            scaleX -= 0.2f
            scaleY -= 0.2f
            img.animate().scaleX(scaleX).scaleY(scaleY).duration = duration.toLong()
        }
    }
    
    • 여기서의 frame은 레이아웃의 frame 말씀하시는건가요? SiRoNYuHa 2018.6.14 13:17
    • 네 맞습니다. pistolcaffe 2018.6.14 13:23
    • 음, 애매하네요. 아예 움직이질 않게 되는 경우도 있고, 움직여도 이전과 다를게 없는 상태가 되는 경우도 있네요 SiRoNYuHa 2018.6.14 13:29
    • 위 예제는 저도 테스트를 해서 직접 되는걸 보고 알려드리는 건데, 아마 테스트 했던 UI 가 다르거나 코드를 잘못적용 하셨거나 일것 같아요. 위 예제를 고대로 적용하지 마시고, 코드내용을 참고하셔서 구현하시고자 하는 UI 환경에 적용하시는게 좋을 듯 합니다 pistolcaffe 2018.6.14 13:39
    • 그대로 적용했다기보다 약간 변화를 줬는데 그게 문제가 되는건지 모르겠네요. 일단 한번 더 도전해보겠습니다~! SiRoNYuHa 2018.6.14 13:43

ᕕ( ᐛ )ᕗ
로그인이 필요합니다

작성한 답변에 다른 개발자들이 댓글을 작성하거나 댓글에 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.