android : 터치 이동시보기 이동 (ACTION_MOVE)


177

내부를 볼 수있는 컨테이너 인 간단한 제어를하고 싶습니다. 컨테이너를 터치하고 손가락을 움직이면 손가락을 따라보기를 이동하고 싶습니다.

어떤 종류의 컨테이너 (레이아웃)를 사용해야합니까? 이것을하는 방법?

표면을 사용할 필요는 없지만 간단한 레이아웃을 사용해야합니다.


답변:


235

이 같은:

public class MyActivity extends Activity implements View.OnTouchListener {

TextView _view;
ViewGroup _root;
private int _xDelta;
private int _yDelta;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    _root = (ViewGroup)findViewById(R.id.root);

    _view = new TextView(this);
    _view.setText("TextView!!!!!!!!");

    RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 50);
    layoutParams.leftMargin = 50;
    layoutParams.topMargin = 50;
    layoutParams.bottomMargin = -250;
    layoutParams.rightMargin = -250;
    _view.setLayoutParams(layoutParams);

    _view.setOnTouchListener(this);
    _root.addView(_view);
}

public boolean onTouch(View view, MotionEvent event) {
    final int X = (int) event.getRawX();
    final int Y = (int) event.getRawY();
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
            _xDelta = X - lParams.leftMargin;
            _yDelta = Y - lParams.topMargin;
            break;
        case MotionEvent.ACTION_UP:
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            break;
        case MotionEvent.ACTION_POINTER_UP:
            break;
        case MotionEvent.ACTION_MOVE:
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
            layoutParams.leftMargin = X - _xDelta;
            layoutParams.topMargin = Y - _yDelta;
            layoutParams.rightMargin = -250;
            layoutParams.bottomMargin = -250;
            view.setLayoutParams(layoutParams);
            break;
    }
    _root.invalidate();
    return true;
}}

에서 main.xml단지 RelativeLayout@+id/root


1
@appserv : 니스 직업! 당신이 넣어하지만 왜 궁금 layoutPrarms.rightMargin = -250와 함께 같은 bottomMargin! 설명 할 수 있습니까? 어쨌든 대단히 감사합니다 !!
Kingfisher Phuoc

1
메모리가 이러한 값없이 제공되는 경우 뷰를 오른쪽 또는 아래쪽으로 이동할 때보기가 압축됩니다. 그것들을 바꾸고 무슨 일이 일어나는지 볼 수 있습니다.
Vyacheslav Shylkin

1
그들은 최종일 필요는 없습니다. 이 변수를 다시 할당하지 않기 위해 최종적으로 만들었습니다.
Vyacheslav Shylkin

1
잘 작동하지만 화면 외부에서 이동을 제한 할 수있는 방법이 있습니까? 즉 화면 경계 내부에서만 이동해야합니다.
Daud Arfin

2
@VyacheslavShilkin이 코드에서 찾은 유일한 문제는 xml 파일에서 레이아웃을 팽창시켜 이동할 수 없다는 것입니다. 이것이 실제로 코드의 문제입니까, 아니면 무지 때문에 누락 되었습니까?
user2498079

356

ViewPropertyAnimator를 사용하여 쉽게 수행 할 수있는 방법을 찾았습니다.

float dX, dY;

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

    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:

            dX = view.getX() - event.getRawX();
            dY = view.getY() - event.getRawY();
            break;

        case MotionEvent.ACTION_MOVE:

            view.animate()
                    .x(event.getRawX() + dX)
                    .y(event.getRawY() + dY)
                    .setDuration(0)
                    .start();
            break;
        default:
            return false;
    }
    return true;
}

3
@ ruan65 화면이 드래그되지 않도록 제한 할 수 있습니까?
Dhiraj Devkar

13
누군가 왜 이것이 작동하는지 혼란 스럽다면 getX ()는보기에 상대적인 X 좌표를 반환하는 반면 getRawX ()는 장치 화면에 대한 절대 좌표를 반환한다는 것을 알고 있습니다. stackoverflow.com/a/20636236/4258848
Amer Mograbi

2
천재, 방금 경계 검사를 추가했으며 슬라이더 버튼의 가로 스크롤에
효과적입니다

2
이것은 이전 답변과 동일하게 작동하지만 이동 이벤트에서 translationX 및 translationY 메소드를 사용하는 것이 좋습니다. 위치를 일정하게 유지하려면 "up"이벤트에서보기의 레이아웃 속성을 설정하십시오. 번역 방법은 휴대 전화의 하드웨어 계층을 사용하고 있습니다. 레이아웃 속성이 아닙니다.
Gillis Haasnoot

3
우리는 또한 사용 setX하고 setY대신 시간 0의 애니메이션을 적용하는 직접
user1032613

11

@Andrey 접근 방식에 따라 뷰를 중앙에서 이동하려면 뷰의 절반 높이와 너비를 이동으로 빼면됩니다.

float dX, dY;

@Override
public boolean onTouchEvent(View view, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            dX = view.getX() - event.getRawX();
            dY = view.getY() - event.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            view.animate()
                .x(event.getRawX() + dX - (view.getWidth() / 2))
                .y(event.getRawY() + dY - (view.getHeight() / 2))
                .setDuration(0)
                .start();
            break;
        default:
            return false;
    }
    return true;
}

3

코 틀린에서 동일한 구현

    rightPanel.setOnTouchListener(View.OnTouchListener { view, event ->
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {

                rightDX = view!!.x - event.rawX
                // rightDY = view!!.getY() - event.rawY;

            }
            MotionEvent.ACTION_MOVE -> {

                var displacement = event.rawX + rightDX

                view!!.animate()
                        .x(displacement)
                        // .y(event.getRawY() + rightDY)
                        .setDuration(0)
                        .start()
            }
            else -> { // Note the block
                return@OnTouchListener false
            }
        }
        true
 })

이 답변에 대한 재미있는 점은 내가 쓴 것으로 여러 번 도움이되었습니다.
Hitesh Sahu

3

용기를 터치하면 화면이 손가락을 따라갑니다.

xml 코드

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/floating_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <ImageView
      android:id="@+id/btn_chat"
      android:layout_width="42dp"
      android:layout_height="42dp"
      />

<LinearLayout>

자바 코드

public class DashBoardActivity extends Activity implements View.OnClickListener, View.OnTouchListener {

    float dX;
    float dY;
    int lastAction;
    LinearLayout floatingLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dashboard);

        floatingLayout = findViewById(R.id.floating_layout);
        floatingLayout.setOnTouchListener(this);    



     @Override
    public boolean onTouch(View view, MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                dX = view.getX() - event.getRawX();
                dY = view.getY() - event.getRawY();
                lastAction = MotionEvent.ACTION_DOWN;
                break;

            case MotionEvent.ACTION_MOVE:
                view.setY(event.getRawY() + dY);
                view.setX(event.getRawX() + dX);
                lastAction = MotionEvent.ACTION_MOVE;
                break;

            case MotionEvent.ACTION_UP:
                if (lastAction == MotionEvent.ACTION_DOWN)
                    Toast.makeText(DashBoardActivity.this, "Clicked!", Toast.LENGTH_SHORT).show();
                break;

            default:
                return false;
        }
        return true;
    }
}

2

view.translationX 및 view.translationY를 사용하여 뷰를 이동하는 것이 좋습니다.

코 틀린 스 니펫 :

yourView.translationX = xTouchCoordinate
yourView.translationY = yTouchCoordinate

2

Kotlin에서 사용자 정의 터치 리스너 클래스를 작성하십시오.

(이 코드는 뷰가 상위 뷰에서 드래그되는 것을 제한합니다)

class CustomTouchListener(
  val screenWidth: Int, 
  val screenHeight: Int
) : View.OnTouchListener {
    private var dX: Float = 0f
    private var dY: Float = 0f

    override fun onTouch(view: View, event: MotionEvent): Boolean {

        val newX: Float
        val newY: Float

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                dX = view.x - event.rawX
                dY = view.y - event.rawY
            }
            MotionEvent.ACTION_MOVE -> {

                newX = event.rawX + dX
                newY = event.rawY + dY

                if ((newX <= 0 || newX >= screenWidth - view.width) || (newY <= 0 || newY >= screenHeight - view.height)) {
                    return true
                }

                view.animate()
                    .x(newX)
                    .y(newY)
                    .setDuration(0)
                    .start()
            }
        }
        return true
    }
}

사용 방법?

parentView.viewTreeObserver.addOnGlobalLayoutListener { view.setOnTouchListener(CustomTouchListener(parentView.width, parentView.height)) }

parentView 보기의 부모입니다.


1

아래 코드에서 RegionView( git 이라는 것을 만들었습니다. ) .이 자식은 중첩 된 각 하위 항목의 드래그 앤 확대 작업을 관리하는 재사용 가능한 컨테이너입니다.

여기서, 우리는 조작 topleft하위의 계수 ViewLayoutParams다이어그램에 대한 시뮬레이션 운동. 드래그 연산으로 이해되는 것과 스케일 연산으로 결정된 것을 처리하는 해석을 분리함으로써 아이의 안정적인 조작을 제공 할 수 있습니다 View.

package com.zonal.regionview;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.RelativeLayout;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Alexander Thomas (@Cawfree) on 20/07/2017.
 */

/** Enables users to customize Regions Of Interest on a Canvas. */
public class RegionView extends RelativeLayout implements View.OnTouchListener, GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener {

    /* Member Variables. */
    private final GestureDetector      mGestureDetector;
    private final ScaleGestureDetector mScaleGestureDetector;
    private final Map<Integer, View>   mViewMap;
    private       boolean              mScaling;
    private       float                mScale;
    private       boolean              mWrapContent;
    private       boolean              mDropOnScale;

    public RegionView(Context context) {
        // Implement the Parent.
        super(context);
        // Initialize Member Variables.
        this.mGestureDetector      = new GestureDetector(context, this);
        this.mViewMap              = new HashMap<>();
        this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
        this.mScaling              = false;
        this.mScale                = Float.NaN;
        this.mWrapContent          = false;
        this.mDropOnScale          = false;
        // Register ourself as the OnTouchListener.
        this.setOnTouchListener(this);
    }

    public RegionView(Context context, @Nullable AttributeSet attrs) {
        // Implement the Parent.
        super(context, attrs);
        // Initialize Member Variables.
        this.mGestureDetector      = new GestureDetector(context, this);
        this.mViewMap              = new HashMap<>();
        this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
        this.mScaling              = false;
        this.mWrapContent          = false;
        this.mDropOnScale          = false;
        // Register ourself as the OnTouchListener.
        this.setOnTouchListener(this);
    }

    public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        // Implement the Parent.
        super(context, attrs, defStyleAttr);
        // Initialize Member Variables.
        this.mGestureDetector      = new GestureDetector(context, this);
        this.mViewMap              = new HashMap<>();
        this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
        this.mScaling              = false;
        this.mWrapContent          = false;
        this.mDropOnScale          = false;
        // Register ourself as the OnTouchListener.
        this.setOnTouchListener(this);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        // Implement the Parent.
        super(context, attrs, defStyleAttr, defStyleRes);
        // Initialize Member Variables.
        this.mGestureDetector      = new GestureDetector(context, this);
        this.mViewMap              = new HashMap<>();
        this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
        this.mScaling              = false;
        this.mWrapContent          = false;
        this.mDropOnScale          = false;
        // Register ourself as the OnTouchListener.
        this.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(final View v, final MotionEvent event) {
        // Calculate the PointerId.
        final int lPointerId = event.getPointerId(event.getActionIndex());
        // Handle the TouchEvent.
        this.getGestureDetector().onTouchEvent(event);
        this.getScaleGestureDetector().onTouchEvent(event);
        // Did the user release a pointer?
        if(event.getAction() == MotionEvent.ACTION_UP) {
            // Was there a View associated with this Action?
            final View lView = this.getViewMap().get(lPointerId);
            // Does the View exist?
            if(lView != null) {
                // Remove the View from the Map.
                this.getViewMap().remove(lPointerId); /** TODO: Provide a Callback? */
            }
        }
        // Consume all events for now.
        return true;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        // Calculate the PointerId.
        final Integer lPointerId = Integer.valueOf(e.getPointerId(e.getActionIndex()));
        // Fetch the View.
        final View    lView      = this.getViewFor(Math.round(e.getRawX()), Math.round(e.getRawY()));
        // Is it valid?
        if(lView != null) {
            // Watch the View.
            this.getViewMap().put(lPointerId, lView);
            // Configure the Anchor.
            lView.setPivotX(0);
            lView.setPivotY(0);
            // Assert that we handled the event.
            return true;
        }
        // Assert that we ignored the event.
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        // Are we not scaling?
        if(!this.isScaling()) {
            // Calculate the PointerId.
            final Integer lPointerId = Integer.valueOf(e1.getPointerId(e1.getActionIndex()));
            // Fetch the View.
            final View    lView      = this.getViewMap().get(lPointerId);
            // Is the scroll valid for a given View?
            if(lView != null) {
                // Calculate the Scaled Width and Height of the View.
                final float lWidth    = lView.getWidth()  * lView.getScaleX();
                final float lHeight   = lView.getHeight() * lView.getScaleY();
                // Declare the initial position.
                final int[] lPosition = new int[] { (int)(e2.getX() - ((lWidth)  / 2)), (int)(e2.getY() - ((lHeight) / 2)) };
                // Are we wrapping content?
                if(this.isWrapContent()) {
                    // Wrap the Position.
                    this.onWrapContent(lPosition, lWidth, lHeight);
                }
                // Update the Drag.
                this.onUpdateDrag(lView, lPosition);
            }
            // Assert we handled the scroll.
            return true;
        }
        // Otherwise, don't permit scrolling. Don't consume the MotionEvent.
        return false;
    }

    /** Forces X/Y values to be coerced within the confines of the RegionView. */
    private final void onWrapContent(final int[] pPosition, final float pWidth, final float pHeight) {
        // Limit the parameters. (Top-Left)
        pPosition[0] = Math.max(pPosition[0], 0);
        pPosition[1] = Math.max(pPosition[1],  0);
        // Limit the parameters. (Bottom-Right)
        pPosition[0] = Math.min(pPosition[0], (int)(this.getWidth()  - pWidth));
        pPosition[1] = Math.min(pPosition[1], (int)(this.getHeight() - pHeight));
    }

    /** Updates the Drag Position of a child View within the Layout. Implicitly, we update the LayoutParams of the View. */
    private final void onUpdateDrag(final View pView, final int pLeft, final int pTop) {
        // Allocate some new MarginLayoutParams.
        final MarginLayoutParams lMarginLayoutParams = new MarginLayoutParams(pView.getLayoutParams());
        // Update the Margin.
        lMarginLayoutParams.setMargins(pLeft, pTop, 0, 0);
        // Refactor the MarginLayoutParams into equivalent LayoutParams for the RelativeLayout.
        pView.setLayoutParams(new RelativeLayout.LayoutParams(lMarginLayoutParams));
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // Calculate the ScaleFactor.
              float lScaleFactor = detector.getScaleFactor() - 1;
        // Fetch the Scaled View.
        final View  lView        = this.getViewMap().entrySet().iterator().next().getValue();
        // Update the ScaleFactor.
        final float lScale       = this.getScale() + lScaleFactor;
        // Calculate the Proposed Width and Height.
        final int   lWidth  = Math.round(lView.getWidth()  * lScale);
        final int   lHeight = Math.round(lView.getHeight() * lScale);
        // Is the View already too large for wrap content?
        if(lWidth >= this.getWidth() || lHeight >= this.getHeight()) {
            // Don't update the scale.
            return false;
        }
        // Persist this Scale for the View.
        lView.setScaleX(lScale);
        lView.setScaleY(lScale);
        // Assign the Scale.
        this.setScale(lScale);
        // Compute the Position.
        final int[] lPosition = new int[] { Math.round(detector.getFocusX()) - (lWidth / 2), Math.round(detector.getFocusY()) - (lHeight / 2) };
        // Are we wrapping the Position?
        if(this.isWrapContent()) {
            // Wrap the Position.
            this.onWrapContent(lPosition, lWidth, lHeight);
        }
        // Update the Drag.
        this.onUpdateDrag(lView, lPosition);
        // Assert that we handled the scale.
        return true;
    }

    /** Update the Drag. */
    private final void onUpdateDrag(final View pView, final int[] pPosition) {
        // Call the sub-implementation.
        this.onUpdateDrag(pView, pPosition[0], pPosition[1]);
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) { 
        // Is the user not dragging at all?
        if(this.getViewMap().size() == 1) {
            // Fetch the View.
            final View lView = this.getViewMap().entrySet().iterator().next().getValue();
            // Initialize the Scale.
            this.setScale(lView.getScaleX()); 
            // Assert that we've started scaling.
            this.setScaling(true);
            // Inform the callback.
            return true;
        }
        // Otherwise, don't allow scaling.
        return false;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        // Were we scaling?
        if(this.isScaling()) {
            // Assert that we've stopped scaling.
            this.setScaling(false);
            // Reset the Scale.
            this.setScale(Float.NaN);
            // Should we stop dragging now that we've finished scaling?
            if(this.isDropOnScale()) {
                // Clear the ViewMap.
                this.getViewMap().clear();
            }
        }
    }

    /** Returns the View colliding with the given co-ordinates. */
    private final View getViewFor(final int pX, final int pY) {
        // Declare the LocationBuffer.
        final int[] lLocationBuffer = new int[2];
        // Iterate the Views.
        for(int i = 0; i < this.getChildCount(); i++) {
            // Fetch the child View.
            final View lView = this.getChildAt(i);
            // Fetch its absolute position.
            lView.getLocationOnScreen(lLocationBuffer);
            // Determine if the MotionEvent collides with the View.
            if(pX > lLocationBuffer[0] && pY > lLocationBuffer[1] && (pX < lLocationBuffer[0] + (lView.getWidth() * lView.getScaleX())) && (pY < lLocationBuffer[1] + (lView.getHeight() * lView.getScaleY()))) {
                // Return the View.
                return lView;
            }
        }
        // We couldn't find a View.
        return null;
    }

    /* Unused Overrides. */
    @Override public void      onShowPress(MotionEvent e) {  }
    @Override public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }
    @Override public void      onLongPress(MotionEvent e) { }
    @Override public boolean       onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; }

    /* Getters and Setters. */
    private final GestureDetector getGestureDetector() {
        return this.mGestureDetector;
    }

    private final ScaleGestureDetector getScaleGestureDetector() {
        return this.mScaleGestureDetector;
    }

    private final Map<Integer, View> getViewMap() {
        return this.mViewMap;
    }

    private final void setScaling(final boolean pIsScaling) {
        this.mScaling = pIsScaling;
    }

    private final boolean isScaling() {
        return this.mScaling;
    }

    private final void setScale(final float pScale) {
        this.mScale = pScale;
    }

    private final float getScale() {
        return this.mScale;
    }

    /** Defines whether we coerce the drag and zoom of child Views within the confines of the Layout. */
    public final void setWrapContent(final boolean pIsWrapContent) {
        this.mWrapContent = pIsWrapContent;
    }

    public final boolean isWrapContent() {
        return this.mWrapContent;
    }

    /** Defines whether a drag operation is considered 'finished' once the user finishes scaling a view. */
    public final void setDropOnScale(final boolean pIsDropOnScale) {
        this.mDropOnScale = pIsDropOnScale;
    }

    public final boolean isDropOnScale() {
        return this.mDropOnScale;
    }

}

다음은 사용 사례의 예입니다.

package com.zonal.regionview;

import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.AnalogClock;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Allocate a RegionView.
        final RegionView lRegionView = new RegionView(this);
        // Add some example items to drag.
        lRegionView.addView(new AnalogClock(this));
        lRegionView.addView(new AnalogClock(this));
        lRegionView.addView(new AnalogClock(this));
        // Assert that we only want to drag Views within the confines of the RegionView.
        lRegionView.setWrapContent(true);
        // Assert that after we've finished scaling a View, we want to stop being able to drag it until a new drag is started.
        lRegionView.setDropOnScale(true);
        // Look at the RegionView.
        this.setContentView(lRegionView);
    }

}

0

수동으로 입력 한 숫자의 종속성을 제거하기 위해 @Vyacheslav Shylkin에서 제공하는 솔루션을 약간 변경했습니다.

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainActivity extends Activity implements View.OnTouchListener
{
    private int       _xDelta;
    private int       _yDelta;
    private int       _rightMargin;
    private int       _bottomMargin;
    private ImageView _floatingView;

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

        this._floatingView = (ImageView) findViewById(R.id.textView);

        this._floatingView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
        {
            @Override
            public boolean onPreDraw()
            {
                if (_floatingView.getViewTreeObserver().isAlive())
                    _floatingView.getViewTreeObserver().removeOnPreDrawListener(this);

                updateLayoutParams(_floatingView);
                return false;
            }
        });

        this._floatingView.setOnTouchListener(this);
    }

    private void updateLayoutParams(View view)
    {
        this._rightMargin = -view.getMeasuredWidth();
        this._bottomMargin = -view.getMeasuredHeight();

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(view.getMeasuredWidth(), view.getMeasuredHeight());
        layoutParams.bottomMargin = this._bottomMargin;
        layoutParams.rightMargin = this._rightMargin;

        view.setLayoutParams(layoutParams);
    }

    @Override
    public boolean onTouch(View view, MotionEvent event)
    {
        if (view == this._floatingView)
        {
            final int X = (int) event.getRawX();
            final int Y = (int) event.getRawY();

            switch (event.getAction() & MotionEvent.ACTION_MASK)
            {
                case MotionEvent.ACTION_DOWN:
                    RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
                    this._xDelta = X - lParams.leftMargin;
                    this._yDelta = Y - lParams.topMargin;
                    break;

                case MotionEvent.ACTION_MOVE:
                    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
                    layoutParams.leftMargin = X - this._xDelta;
                    layoutParams.topMargin = Y - this._yDelta;
                    layoutParams.rightMargin = this._rightMargin;
                    layoutParams.bottomMargin = this._bottomMargin;
                    view.setLayoutParams(layoutParams);
                    break;
            }

            return true;
        }
        else
        {
            return false;
        }
    }
}

0

이 예제에서는 크기, 완벽한 애니메이션 및 클릭 포착에 관계없이 상위 범위 내에서 뷰를 이동할 수 있습니다.

이 솔루션이 다른 의견보다 우월한 이유는이 접근 방식이 방향 패드 를 사용하여 많은 버그의 소스 인 View 위치에서 릴레이하지 않기 때문입니다.

View view;
Animator.AnimatorListener listener;
boolean onMove = false;
boolean firstAnimation = true;
static final int CLICK_DURATION = 175;

float parentWidth;
float parentHeight;

// Those are the max bounds
// within the contianer
float xBoundMax;
float yBoundMax;

// This variables hold the target
// ordinates for the next
// animation in case an animation
// is already in progress.
float targetX;
float targetY;

float downRawX;
float downRawY;

public boolean onTouchEvent(MotionEvent event)
{
    switch (event.getAction())
    {
        case MotionEvent.ACTION_UP:
            if(event.getEventTime() - event.getDownTime() < CLICK_DURATION) click();
            onMove = false;
            break;
        case MotionEvent.ACTION_DOWN:
            firstAnimation = true;
            xBoundMax = parentWidth - view.getWidth();
            yBoundMax = parentHeight - view.getHeight();
            downRawX = event.getRawX();
            downRawY = event.getRawY();
            break;

        case MotionEvent.ACTION_MOVE:
            if(!onMove)
            {
                if(event.getEventTime() - event.getDownTime() < CLICK_DURATION) break;
                else onMove = true;
            }

            // Calculating the position the
            // view should be posed at.
            float offsetX = event.getRawX() - downRawX;
            float offsetY = event.getRawY() - downRawY;
            downRawX = event.getRawX();
            downRawY = event.getRawY();
            targetX = currentX + offsetX;
            targetY = currentY.getY() + offsetY;

            // Checking if view
            // is within parent bounds
            if(targetX > parentWidth - view.getWidth()) targetX = xBoundMax;
            else if (targetX < 0) targetX = 0;
            if(targetY > parentHeight - view.getHeight())targetY = yBoundMax;
            else if (targetY < 0) targetY = 0;

            // This check is becuase the user may just click on the view
            // So if it's a not a click, animate slowly but fastly
            // to the desired position
            if(firstAnimation)
            {
                firstAnimation = false;
                animate(70, getNewAnimationListener());
                break;
            }

            if(listener != null) break;
            animate(0, null);
            break;

            case MotionEvent.ACTION_BUTTON_PRESS:
        default:
            return false;
    }
    return true;
}

// Gets a new animation listener and reset
private android.animation.Animator.AnimatorListener getNewAnimationListener()
{
    listener =  new Animator.AnimatorListener() {
        @Override public void onAnimationStart(Animator animation) { }
        @Override public void onAnimationCancel(Animator animation) { }
        @Override public void onAnimationRepeat(Animator animation) { }
        @Override public void onAnimationEnd(Animator animation) {
            animation.removeListener(listener);
            listener = null;
            view.setAnimation(null);
            animate(0, null);
        }
    };
    return listener;
}

private void animate(int duration, @Nullable Animator.AnimatorListener listener)
{
    view.animate()
            .x(targetX)
            .y(targetY)
            .setDuration(duration)
            .setListener(listener)
            .start();
    currentX = targetX;
    currentY = targetY;
}

private void click()
{
    // Dohere stuff that you desire and than
}

컨테이너가 ScrollView 또는 이중 차원 ScrollView 내에있는 경우이 줄을 onTouch에 추가해야합니다

view.getParent().requestDisallowInterceptTouchEvent(true);

0

@ Alex Karshin 의 답변과 마찬가지로 조금 변경합니다.

public class MovingObject implements OnTouchListener {
private RelativeLayout.LayoutParams lParams;
private PointF viewPoint, prePoint, currPoint;

public MovingObject() {
    lParams = null;
    viewPoint = new PointF();
    prePoint = new PointF();
    currPoint = new PointF();
}

public boolean onTouch(View view, MotionEvent event) {
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
        viewPoint.set(view.getX(), view.getY());
        prePoint.set(event.getRawX(), event.getRawY());
        lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
        break;
    case MotionEvent.ACTION_UP:
        break;
    case MotionEvent.ACTION_POINTER_DOWN:
        break;
    case MotionEvent.ACTION_POINTER_UP:
        break;
    case MotionEvent.ACTION_MOVE:
        currPoint.set(event.getRawX(), event.getRawY());
        moveToCurrentPoint(view);
        break;
    }
    view.invalidate();
    return true;
}

private void moveToCurrentPoint(View view) {
    float dx = currPoint.x - prePoint.x - prePoint.x + viewPoint.x;
    float dy = currPoint.y - prePoint.y - prePoint.y + viewPoint.y;
    lParams.leftMargin = (int) (prePoint.x + dx);
    lParams.topMargin = (int) (prePoint.y + dy);
    view.setLayoutParams(lParams);
}
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.