Перемещение различных объектов часто применяется в мобильных играх и различных мультимедиа-приложениях. В этой статье мы рассмотрим, как добавлять изображения на экран и свободно перемещать их.
Для этого нам понадобится выполнить следующие действия:
- Создать разметку с элементом FrameLayout.
- Создать программно ImageView для добавленного изображения и установить ему OnTouchListener
- В методе onTouch() определить действия пользователя
- Менять значения LayoutParams в соответствии с текущим расположением ImageView
Для начала создадим новый проект с пустой активностью.
В разметке активности activity_main.xml удалим старую разметку и заменим её на новую. FrameLayout в данном случае будет использоваться как контейнер для добавленных изображений.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/btn_add"
android:layout_alignParentTop="true"
/>
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Добавить изображение"
/>
</RelativeLayout>
Определим созданные элементы разметки в коде активности MainActivity.
public class MainActivity extends AppCompatActivity {
private FrameLayout container;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
container = findViewById(R.id.container);
Button btn_add = findViewById(R.id.btn_add);
}
Для выбора и обрезки добавляемых изображений воспользуемся библиотекой uCrop, которую можно найти на GitHub. Подключим её, добавив в файл build.gradle проекта следующие строки:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
Затем в файле build.gradle модуля приложения нужно подключить саму библиотеку:
dependencies {
...
implementation 'com.github.yalantis:ucrop:2.2.2-native'
}
После этого в манифест приложения AndroidManifest.xml нужно добавить активность библиотеки.
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
Теперь нужно получить изображения для добавления в приложение. С помощью интента вызовем файловый менеджер, с помощью которого найдём подходящие изображения на устройстве. Для этого переопределим обработчик нажатий у кнопки следующим образом.
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
btn_add.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_IMAGE);
}
});
}
Чтобы получить Uri выбранного файла, нужно переопределить в коде активности метод onActivityResult(), добавив в него следующий код:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_IMAGE: {
if (data != null && data.getData() != null) {
startCrop(data.getData());
}
break;
}
}
}
}
private void startCrop(Uri data) {
String name = getFileName(data);
File file = new File(getCacheDir(), name);
filename = name;
}
Метод getFileName() используется, чтобы получить из Uri имя файла, посмотреть его можно будет в исходном коде проекта далее.
Когда у нас есть Uri выбранного файла, мы можем воспользоваться библиотекой uCrop, чтобы обрезать изображение так, как нам требуется. Это можно сделать с помощью следующих строк в методе startCrop().
private void startCrop(Uri data) {
...
UCrop uCrop = UCrop.of(data, Uri.fromFile(file));
uCrop.start(this);
}
После вызова start() откроется активность библиотеки, в которой можно работать с изображением. Как только работа будет закончена, в нашу активность вернётся интент с Uri обрезанного изображения. Нам, в данном случае, будет проще обратиться к самому файлу, так как его путь и имя известны. Получить его можно всё в том же onActivityResult() следующим образом.
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
...
case UCrop.REQUEST_CROP: {
createImageView();
break;
}
}
}
}
private void createImageView() {
File file = new File(getCacheDir(), filename);
Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath());
ImageView imageView = new ImageView(MainActivity.this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(250, 250);
params.leftMargin = 0;
params.topMargin = 0;
imageView.setLayoutParams(params);
imageView.setImageBitmap(bmp);
container.addView(imageView);
file.delete();
}
Здесь же мы сразу создаём новый ImageView, которому задаём размер и загружаем в него Bitmap, полученный из файла.
Сейчас изображения просто добавляются на экран и с ними нельзя никак взаимодействовать. Чтобы обрабатывать касания для изображений, добавим в код активности переменную OnTouchListener, в которой будет переопределяться метод onTouch(). Этот метод позволяет отлавливать различные касания и жесты, нам же нужны только следующие действия пользователя:
- Пользователь коснулся изображения;
- Пользователь двигает пальцем по экрану;
- Пользователь убрал палец с экрана;
Таким образом, код обработка будет выглядеть следующим образом:
private int xDelta, yDelta;
private View.OnTouchListener touchListener = new View.OnTouchListener() {
@Override 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: {
FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) view.getLayoutParams();
xDelta = x - lParams.leftMargin;
yDelta = y - lParams.topMargin;
break;
}
case MotionEvent.ACTION_UP: {
Toast.makeText(getApplicationContext(), "Объект перемещён", Toast.LENGTH_SHORT).show();
break;
}
case MotionEvent.ACTION_MOVE: {
if (x - xDelta + view.getWidth() <= container.getWidth()
&& y - yDelta + view.getHeight() <= container.getHeight()
&& x - xDelta >= 0
&& y - yDelta >= 0) {
FrameLayout.LayoutParams layoutParams =
(FrameLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = x - xDelta;
layoutParams.topMargin = y - yDelta;
layoutParams.rightMargin = 0;
layoutParams.bottomMargin = 0;
view.setLayoutParams(layoutParams);
}
break;
}
}
container.invalidate();
return true;
}
};
Здесь мы определяем текущие координаты изображения, когда пользователь касается его, затем вычисляем изменение координат и обновляем их у изображения, когда пользователь убирает палец с изображения. Также здесь присутствует условие, благодаря которому изображение не должно уходить за пределы экрана.
Теперь остаётся только задать этот обработчик касаний создаваемым ImageView в createImageView().
private void createImageView() {
...
imageView.setOnTouchListener(touchListener);
container.addView(imageView);
file.delete();
}
На этом всё. Теперь, запустив приложение, мы можем свободно перемещать изображения по экрану.
Таким образом, с помощью нескольких строк кода, можно создать простое приложение для взаимодействия с изображениями в экране.
Посмотреть исходный код приложения можно, перейдя по ссылке на GitHub.





Здравствуйте а как сделать без добавлений фотографий а чтоб уже был объект на экране?
Здравствуйте! Если нужно, чтобы изображения сразу были на экране, можно загрузить нужные изображения в папку проекта res/drawable, затем в разметке создать нужное количество ImageView и указать им в свойстве android:src выбранные изображения. Тогда при запуске приложения они уже будут находиться на экране.