Android - move an ImageView on screen (Like dragging)

前端 未结 6 1195
灰色年华
灰色年华 2021-02-05 15:34

I\'m trying to create an app that can move an ImageView on your device like dragging and when I put like 75% of the ImageView out of the screen show a

相关标签:
6条回答
  • 2021-02-05 16:08

    I'm using this method to drag an ImageView , I hope that this can help you : So I defined those attributes of the class :

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

    Then I implement this code under the OnCreate() method of the activity :

     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;
            }
        });
    
    0 讨论(0)
  • 2021-02-05 16:15

    Update

    Add right/bottom margin in step 3 to prevent image zoomed. You can see if you not change right/bottom margin, image will zoomed by relative layout.

    1. getMeasuredHeight/Width avoid MATCH_PARENT and WRAP_CONTENT.
    2. If there is a toolbar/actionbar then topMargin + height > relativeLayout's height also applies to out of bottom determination.
    3. record state of out of bound avoid toast continuous appear.

      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;
          }
      }
      
    0 讨论(0)
  • 2021-02-05 16:23

    The framework has a class called View.OnDragListener. See the Drag & Drop tutorial.

    See also additionally the DraggablePanel project if you want to study how this can be done.

    0 讨论(0)
  • 2021-02-05 16:26

    A working example of how to move all the views contained in a RelativeLayout using onTouch. Hope it will help :

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-05 16:29

    Your routine works for the most part. In the following code, I have commented out sections that are not needed and made notations for those parts that need some explanation. Here is what the finished product looks like:

    This graphic explains how the left margin is calculated. The same type of calculation applies to the top margin.

    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>
    
    0 讨论(0)
  • 2021-02-05 16:31

    You can achieve this via this code.

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

    Now in your onTouch method, calculate if the target location exceeds the above dimensions.

    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 
    
    }
    
    0 讨论(0)
提交回复
热议问题