web-dev-qa-db-ja.com

Android-画面上でImageViewを移動します(ドラッグのように)

私は、デバイス上でImageViewをドラッグのように移動できるアプリを作成しようとしています。たとえば、ImageViewの75%を画面の外に配置すると、Toastが表示されます。私はMotionEventonTouchListenerについて読んでいて、これに従いました question ですが、それは私を納得させません。

編集する

私の現在のコードは:

_public class MainActivity extends AppCompatActivity implements View.OnTouchListener {

    int windowwidth;
    int windowheight;
    private ImageView mImageView;
    private ViewGroup mRrootLayout;
    private int _xDelta;
    private int _yDelta;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DisplayMetrics displaymetrics = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        windowwidth = displaymetrics.widthPixels;
        windowheight = displaymetrics.heightPixels;
        mRrootLayout = (ViewGroup) findViewById(R.id.root);
        mImageView = (ImageView) mRrootLayout.findViewById(R.id.im_move_zoom_rotate);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);
        mImageView.setLayoutParams(layoutParams);
        mImageView.setOnTouchListener(this);
    }
    public boolean onTouch(View view, MotionEvent event) {
        final int X = (int) event.getRawX();
        final int Y = (int) event.getRawY();
        if(X == 0){
            Toast.makeText(this, "OUT", Toast.LENGTH_SHORT).show();
        }
        else if (Y == 0){
            Toast.makeText(this, "OUT", Toast.LENGTH_SHORT).show();
        }
        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;
        }
        mRrootLayout.invalidate();
        return true;
    }
}
_

ifと_else if_を実行したのは、ImageViewがデバイスから外に出ているかどうかを確認するためです。デバイスの左側と右側に問題がないように見えますが、ハードでなく、クリーンにしたいと思います。また、私はLayoutParams(150,150)を取得しません。なぜ150ですか?また、なぜ_RelativeLayout.LayoutParams_を作成する必要があるのか​​、なぜ

_layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
_

ユーザーがImageViewをデバイスから外したいときに削除したいので_if/else if_を実行しました。そのため、彼がしようとしたときに制御する必要があります。現時点では、TOP/LEFT/RIGHTだけがダウンしていません、XまたはYが高さと同じであるか、またはToastを表示するだけであるかどうかを試すために、デバイスの寸法も取得しますが、正しく実行していません。

現在、ImageViewは_ic_launcher_ですが、大きくなります(ほぼ中央の画面)。

注意

それをより簡単または最もクリーンにする他の方法を知っている場合は、ここに自由に置いてください。私のコードは気にせず、適応できます。明確にして、ハードコーディングしないでください。

13
Skizo-ozᴉʞS

ルーチンはほとんどの部分で機能します。次のコードでは、不要なセクションをコメントアウトし、説明が必要な部分を表記しています。完成品は次のようになります。

enter image description here

この図は、左マージンの計算方法を説明しています。同じタイプの計算が上部マージンに適用されます。

enter image description here

MainActivity.Java

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {

    int windowwidth; // Actually the width of the RelativeLayout.
    int windowheight; // Actually the height of the RelativeLayout.
    private ImageView mImageView;
    private ViewGroup mRrootLayout;
    private int _xDelta;
    private int _yDelta;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // We are interested when the image view leaves its parent RelativeLayout
        // container and not the screen, so the following code is not needed.
//        DisplayMetrics displaymetrics = new DisplayMetrics();
//        this.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
//        windowwidth = displaymetrics.widthPixels;
//        windowheight = displaymetrics.heightPixels;
        mRrootLayout = (ViewGroup) findViewById(R.id.root);
        mImageView = (ImageView) mRrootLayout.findViewById(R.id.im_move_zoom_rotate);

        // These these following 2 lines that address layoutparams set the width
        // and height of the ImageView to 150 pixels and, as a side effect, clear any
        // params that will interfere with movement of the ImageView.
        // We will rely on the XML to define the size and avoid anything that will
        // interfere, so we will comment these lines out. (You can test out how a layout parameter
        // can interfere by setting Android:layout_centerInParent="true" in the ImageView.
//        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);
//        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 150);
//        mImageView.setLayoutParams(layoutParams);
        mImageView.setOnTouchListener(this);

        // Capture the width of the RelativeLayout once it is laid out.
        mRrootLayout.post(new Runnable() {
            @Override
            public void run() {
                windowwidth = mRrootLayout.getWidth();
                windowheight = mRrootLayout.getHeight();
            }
        });
    }

    // Tracks when we have reported that the image view is out of bounds so we
    // don't over report.
    private boolean isOutReported = false;

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

        // Check if the image view is out of the parent view and report it if it is.
        // Only report once the image goes out and don't stack toasts.
        if (isOut(view)) {
            if (!isOutReported) {
                isOutReported = true;
                Toast.makeText(this, "OUT", Toast.LENGTH_SHORT).show();
            }
        } else {
            isOutReported = false;
        }
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                // _xDelta and _yDelta record how far inside the view we have touched. These
                // values are used to compute new margins when the view is moved.
                _xDelta = X - view.getLeft();
                _yDelta = Y - view.getTop();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
                // Do nothing
                break;
            case MotionEvent.ACTION_MOVE:
                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view
                    .getLayoutParams();
                // Image is centered to start, but we need to unhitch it to move it around.
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    lp.removeRule(RelativeLayout.CENTER_HORIZONTAL);
                    lp.removeRule(RelativeLayout.CENTER_VERTICAL);
                } else {
                    lp.addRule(RelativeLayout.CENTER_HORIZONTAL, 0);
                    lp.addRule(RelativeLayout.CENTER_VERTICAL, 0);
                }
                lp.leftMargin = X - _xDelta;
                lp.topMargin = Y - _yDelta;
                // Negative margins here ensure that we can move off the screen to the right
                // and on the bottom. Comment these lines out and you will see that
                // the image will be hemmed in on the right and bottom and will actually shrink.
                lp.rightMargin = view.getWidth() - lp.leftMargin - windowwidth;
                lp.bottomMargin = view.getHeight() - lp.topMargin - windowheight;
                view.setLayoutParams(lp);
                break;
        }
        // invalidate is redundant if layout params are set or not needed if they are not set.
//        mRrootLayout.invalidate();
        return true;
    }

    private boolean isOut(View view) {
        // Check to see if the view is out of bounds by calculating how many pixels
        // of the view must be out of bounds to and checking that at least that many
        // pixels are out.
        float percentageOut = 0.50f;
        int viewPctWidth = (int) (view.getWidth() * percentageOut);
        int viewPctHeight = (int) (view.getHeight() * percentageOut);

        return ((-view.getLeft() >= viewPctWidth) ||
            (view.getRight() - windowwidth) > viewPctWidth ||
            (-view.getTop() >= viewPctHeight) ||
            (view.getBottom() - windowheight) > viewPctHeight);
    }
}

activity_main.xml

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/root"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        Android:id="@+id/im_move_zoom_rotate"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_centerHorizontal="true"
        Android:layout_centerVertical="true"
        Android:src="@drawable/circle" />

</RelativeLayout>
6
Cheticamp

私はこのメソッドを使用してImageViewをドラッグしていますが、これがあなたの助けになることを願っています:クラスのこれらの属性を定義しました:

 private float xCoOrdinate, yCoOrdinate;
 private double screenCenterX, screenCenterY;

次に、アクティビティのOnCreate()メソッドの下にこのコードを実装します。

 mRrootLayout.getBackground().setAlpha(255);

    /**
     * Calculate max hypo value and center of screen.
     */
    final DisplayMetrics display = getResources().getDisplayMetrics();
    screenCenterX = display.widthPixels / 2;
    screenCenterY = (display.heightPixels - getStatusBarHeight()) / 2;
    final double maxHypo = Math.hypot(screenCenterX, screenCenterY);

    mImageView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            /**
             * Calculate hypo value of current imageview position according to center
             */
            double centerYPos = mImageView.getY() + (mImageView.getHeight() / 2);
            double centerXPos = mImageView.getX() + (mImageView.getWidth() / 2);
            double a = screenCenterX - centerXPos;
            double b = screenCenterY - centerYPos;
            double hypo = Math.hypot(a, b);

            /**
             * change alpha of background of layout
             */
            alpha = (int) (hypo * 255) / (int) maxHypo;
            if (alpha < 255)
                mRrootLayout.getBackground().setAlpha(255 - alpha);

            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    xCoOrdinate = mImageView.getX() - event.getRawX();
                    yCoOrdinate = mImageView.getY() - event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mImageView.animate().x(event.getRawX() + xCoOrdinate).y(event.getRawY() + yCoOrdinate).setDuration(0).start();
                    break;
                case MotionEvent.ACTION_UP:
                     if (alpha > 50) {
                        Toast.makeText(ImageViewerActivity.this, "Out", Toast.LENGTH_SHORT).show();
                        return false;
                    } else {
                        Toast.makeText(ImageViewerActivity.this, "In", Toast.LENGTH_SHORT).show();
                        mImageView.animate().x(0).y((float) screenCenterY - mImageView.getHeight() / 2).setDuration(100).start();
                        mRrootLayout.getBackground().setAlpha(255);
                    }
                default:
                    return false;
            }
            return true;
        }
    });
3
Imene Noomene

OnTouchを使用してRelativeLayoutに含まれるすべてのビューを移動する方法の実例。それが役立つことを願っています:

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
    private RelativeLayout mRelLay;
    private float mInitialX, mInitialY;
    private int mInitialLeft, mInitialTop;
    private View mMovingView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRelLay = (RelativeLayout) findViewById(R.id.relativeLayout);

        for (int i = 0; i < mRelLay.getChildCount(); i++)
            mRelLay.getChildAt(i).setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        RelativeLayout.LayoutParams mLayoutParams;

        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mMovingView = view;
                mLayoutParams = (RelativeLayout.LayoutParams) mMovingView.getLayoutParams();
                mInitialX = motionEvent.getRawX();
                mInitialY = motionEvent.getRawY();
                mInitialLeft = mLayoutParams.leftMargin;
                mInitialTop = mLayoutParams.topMargin;
                break;

            case MotionEvent.ACTION_MOVE:
                if (mMovingView != null) {
                    mLayoutParams = (RelativeLayout.LayoutParams) mMovingView.getLayoutParams();
                    mLayoutParams.leftMargin = (int) (mInitialLeft + motionEvent.getRawX() - mInitialX);
                    mLayoutParams.topMargin = (int) (mInitialTop + motionEvent.getRawY() - mInitialY);
                    mMovingView.setLayoutParams(mLayoutParams);
                }
                break;

            case MotionEvent.ACTION_UP:
                mMovingView = null;
                break;
        }

        return true;
    }
}
3
Lluis Felisart

フレームワークには、 View.OnDragListenerドラッグアンドドロップ チュートリアルを参照してください。

これがどのように行われるかを研究したい場合は、さらに DraggablePanel プロジェクトも参照してください。

3
Y.S

更新

手順3で右/下マージンを追加して、画像がズームされないようにします。右/下マージンを変更しない場合は、相対レイアウトで画像が拡大されます。 preview solution old and new

  1. getMeasuredHeight/WidthはMATCH_PARENTおよびWRAP_CONTENTを回避します。
  2. ツールバー/アクションバーがある場合は、topMargin + height > relativeLayout's heightもボトム外判定に適用されます。
  3. out of boundの状態を記録すると、トーストが連続して表示されなくなります。

    public class MainActivity extends AppCompatActivity implements View.OnTouchListener {
    
        Point lastPoint = new Point();
        RelativeLayout relativeLayout;
    
        boolean lastOutOfTop = false;
        boolean lastOutOfLeft = false;
        boolean lastOutOfRight = false;
        boolean lastOutOfBottom = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            (findViewById(R.id.imageView)).setOnTouchListener(this);
            relativeLayout = (RelativeLayout)findViewById(R.id.relativeLayout);
        }
    
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            //1. user's finger
            final Point point = new Point((int) event.getRawX(), (int) event.getRawY());
    
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    // 2. record the last touch point
                    lastPoint = point;
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 3. get the move offset
                    final Point offset = new Point(point.x-lastPoint.x, point.y-lastPoint.y);
                    RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view
                            .getLayoutParams();
                    layoutParams.leftMargin += offset.x;
                    layoutParams.topMargin += offset.y;
                    // * also check right/bottom Margin
                    layoutParams.rightMargin = relativeLayout.getMeasuredWidth() - layoutParams.leftMargin+view.getMeasuredWidth();
                    layoutParams.bottomMargin = relativeLayout.getMeasuredHeight() - layoutParams.topMargin+view.getMeasuredHeight();
                    view.setLayoutParams(layoutParams);
                    // 4. record the last touch point
                    lastPoint = point;
                    break;
            }
    
            // 5. check bounds
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
            boolean outOfTop = layoutParams.topMargin < 0;
            boolean outOfLeft = layoutParams.leftMargin < 0;
            boolean outOfBottom = layoutParams.topMargin+view.getMeasuredHeight() > relativeLayout.getMeasuredHeight();
            boolean outOfRight = layoutParams.leftMargin+view.getMeasuredWidth() > relativeLayout.getMeasuredWidth();
    
            // 6. if out of bound
            if (outOfLeft&&!lastOutOfLeft) Toast.makeText(this, "OUT Left", Toast.LENGTH_SHORT).show();
            if (outOfTop&&!lastOutOfTop) Toast.makeText(this, "OUT Top", Toast.LENGTH_SHORT).show();
            if (outOfBottom&&lastOutOfBottom) Toast.makeText(this, "OUT Bottom", Toast.LENGTH_SHORT).show();
            if (outOfRight&&lastOutOfRight)  Toast.makeText(this, "OUT Right", Toast.LENGTH_SHORT).show();
    
            // 7. record
            lastOutOfTop = outOfTop;
            lastOutOfLeft = outOfLeft;
            lastOutOfBottom = outOfBottom;
            lastOutOfRight = outOfRight;
            return true;
        }
    }
    
2
Codus

このコードを使用してこれを実現できます。

DisplayMetrics metrics = getResources().getDisplayMetrics();
int windowWidth = metrics.widthPixels;
int windowHeight = metrics.heightPixels;

次にonTouchメソッドで、ターゲットの場所が上記の寸法を超えているかどうかを計算します。

if(currentXLocation + deltaX> windowWidth){

// this will ensure that target location 
// is always <= windowHeight
deltaX = windowWidth - currentXLocation; 

} else if( currentXLocation + deltaX < 0){

deltaX = -(currentXLocation);

} else if (...){

// perform similar calculations for the rest 

}
1
Abdul Ahad