Перемещение различных объектов часто применяется в мобильных играх и различных мультимедиа-приложениях. В этой статье мы рассмотрим, как добавлять изображения на экран и свободно перемещать их.
Для этого нам понадобится выполнить следующие действия:
- Создать разметку с элементом 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 выбранные изображения. Тогда при запуске приложения они уже будут находиться на экране.