Android - перемещение ImageView на экране (например, перетаскивание)
Я пытаюсь создать приложение, которое может перемещать ImageView
на вашем устройстве, например, перетаскивание, и когда я помещаю, например, 75% ImageView
на экран показывает Toast
. Я читал о MotionEvent
и onTouchListener
, и я следил за этим 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
из устройства, на левой и правой стороне устройства, похоже, все в порядке, но я хотел бы сделать это чище и не hardwritted, также я не получаю LayoutParams(150,150)
почему 150? Также я не понимаю, почему мне нужно создать RelativeLayout.LayoutParams
и почему я должен поставить
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
Я сделал if/else if
, потому что хочу удалить, когда пользователь хочет вывести ImageView
из устройства, поэтому мне нужно контролировать, когда он пытается, на данный момент я получил только TOP/LEFT/ПРАВИЛЬНО не вниз, я также получаю размеры моего устройства, чтобы попытаться, если X или Y совпадают с высотой или widht, просто покажите Toast
, но он не делает это правильно.
Теперь мой ImageView
- это ic_launcher
, но он будет больше (почти средний экран).
Примечание
Если вы знаете какой-либо другой способ сделать это проще или чище, не стесняйтесь его размещать здесь, меня не интересует мой код, я могу его адаптировать, я просто хочу, чтобы он был ясным и не был жестко закодирован.
Ответы
Ответ 1
Ваша работа работает по большей части. В следующем коде я прокомментировал разделы, которые не нужны, и сделанные обозначения для тех частей, которые нуждаются в некотором объяснении. Вот как выглядит готовый продукт:
![введите описание изображения здесь]()
В этом графике объясняется, как вычисляется левое поле. Тот же тип расчета применяется к верхнему краю.
![введите описание изображения здесь]()
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>
Ответ 2
У рамки есть класс под названием View.OnDragListener
. См. Учебник Drag and Drop.
См. также проект DraggablePanel
, если вы хотите изучить, как это можно сделать.
Ответ 3
Я использую этот метод для перетаскивания 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;
}
});
Ответ 4
Рабочий пример того, как перемещать все представления, содержащиеся в RelativeLayout, используя onTouch. Надеюсь, это поможет:
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;
}
}
Ответ 5
Вы можете достичь этого с помощью этого кода.
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
}
Ответ 6
Обновление
Добавьте правое/нижнее поле на шаге 3, чтобы предотвратить увеличение изображения.
Вы можете увидеть, не измените ли вы правое/нижнее поле, изображение будет увеличено по относительной компоновке.
![предварительное решение для предварительного просмотра старого и нового]()
- getMeasuredHeight/Width избегают
MATCH_PARENT
и WRAP_CONTENT
.
- Если есть панель инструментов/панель действий, тогда
topMargin + height > relativeLayout height
также применяется к определению нижней части.
-
Состояние записи 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 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;
}
}