- GalleryView in Android with Example
- Way to define a Gallery tag
- Important methods of the GalleryView in android
- Attributes of GalleryView
- Example
- Step by Step Implementation
- How to build an image gallery app — full tutorial with code
- Introducing RecyclerView
- Creating your list of images
- Using the adapter
- Как мы сделали свою библиотеку Android Gallery для просмотра медиаконтента
- Начало
- Реализуем библиотеку
- Реализуем функциональность
- Изучаем touch events для реализации swipe to dismiss
- Реализуем дебаунс в PhotoView
- Исправляем внезапный баг в PhotoView
- Вместо заключения
- Судьба пулл реквестов
- Где найти библиотеку
- Что дальше
- Ссылки
GalleryView in Android with Example
In Android, Gallery is a view that can show items in a center locked, horizontal scrolling list, and hence the user can able to select a view, and then the user selected view will be shown in the center of the Horizontal list. “N” number of items can be added by using the Adapter. The adapter is a bridging component between UI component and data source(It can be an array of items defined in java code or from a database). The items given in the adapter will be shown in the gallery in the example.
Important Point: Gallery class was deprecated in API level 16. Instead other horizontally scrolling widgets are HorizontalScrollView and ViewPager from the support library are available.
Way to define a Gallery tag
Important methods of the GalleryView in android
Description
To set the duration for how long a transition animation should run
(in milliseconds) whenever there is change in layout.
This can be set in xml also via android:animationDuration=”3000″
To set the spacing between items in a Gallery. This can be set in xml
also via android:spacing=”5dp”
To set the alpha on the items that are not selected. This can be set in xml
also via android:unselectedAlpha=”0.25″
Let us see the implementation of important methods:
Attributes of GalleryView
Description
To set the background of a Gallery. For background, either we can
set colors (using colors.xml) or images which are kept under drawable folder
Via java code also, we can set the background color in the below way
simpleGallery.setBackgroundColor(Color.GFGGreencolor); // set the desired color
To set the duration for how long a transition animation should run (in milliseconds)
simpleGallery.setAnimationDuration( );
To set the spacing between items in a Gallery.
simpleGallery.setSpacing(10); // We can set spacing between items as per our requirement
To set the alpha on the items that are not selected.
simpleGallery.setUnselectedAlpha(0.25f)
Example
A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Java language.
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.
Источник
How to build an image gallery app — full tutorial with code
There are many reasons you might find yourself needing to create an image gallery – whether it’s to show off album covers for a music app, to present feature images for articles in a feed, or to showcase your work in a portfolio. To make the right impression though, these apps should allow users to effortlessly swipe through multiple images without slowdown and that’s where things get a little tricky.
This tutorial will show you how to create a seamless gallery filled with nice big images and then adapt that for a number of different applications. Along the way, we’ll see how to use RecyclerViews, adapters and Picasso – so hopefully it will make for a great learning exercise, whatever you end up doing with it! Full code and project included below…
Introducing RecyclerView
To create our Android gallery, we’re going to use something called a RecyclerView. This is a handy view that acts very much like a ListView but with the advantage of allowing us to scroll quickly through large data sets. It does this by only loading the images that are currently in view at any given time. This means we can load more images without the app becoming very slow. There’s a lot more that you can do with this view and it’s used all over Google’s own apps, so check out the full explanation to using RecyclerView to find out more.
The good news is that this is all we really need to create our gallery – a RecyclerView filled with images. The bad news is that the RecyclerView is a little more complicated than most other views. Because of course it is.
RecyclerView is not, for starters, available to drag and drop using the design view. So we’ll just have to add it to the activity_main.xml, like so:
Notice that we’re referencing the Android Support Library. This means we also need to modify our build.gradle in order to include the dependency. Just add this line to the app level file:
And if that’s not installed, then you’re going to have to open the SDK manager and install it. Fortunately, Android Studio is pretty smart about prompting you to do all this. I just got a new computer, so I can play along with you!
Head back to the XML and it should now be working just fine. Except the list is not populated except with ‘item 1, item 2, item 3’. What we need to do, is load our images into here.
Creating your list of images
As mentioned, populating our recycler view is a little more complicated than using a regular list. By which, I mean it’s way more complicated… but it’s a great chance for us to learn some handy new skills. So there’s that.
For a RecyclerView, we’re also going to need a layout manager and an adapter. This is what’s going to allow us to organize the information in our view and add the images. We’ll start by initializing our views and attaching an adapter in the onCreate of MainActivity.java. This looks like so:
We’re setting the layout as activity_main, then we’re finding the RecyclerView and initializing it. Notice that we use HasFixedSize to make sure that it won’t stretch to accommodate the content. We’re also creating the layout manager and the adapter here. There are multiple types of layout manager but true to gallery-form, we’re going to pick a grid rather than a long list. Remember to import the GridLayoutManager and the RecyclerView as Android Studio prompts you to do so. Meanwhile, when you highlight MyAdapter, you’ll be given the option to ‘Create Class MyAdapter’. Go for it – make your own MyAdapter.Java and then switch back. We’ll come back to this later.
Before we can use the new adapter class, we first need to create our data set. This is going to take the form of an array list. So in other words, we’re going to place a list of all our images in here, which the adapter will then read and use to fill out the RecyclerView.
Just to make life a little more complicated, creating the Array List is also going to require a new class. First though, create a string array and an integer array in the same MainActivity.Java:
The strings can be anything you want – these will be the titles of your images. As for the integers, these are image IDs. This means they need to point to images in your Drawables folder. Drop some images into there that aren’t too massive and make sure the names are all correct.
Remember: a list is a collection of variables (like strings or integers), whereas an array is more like a filing cabinet of variables. By creating an ArrayList then, we’re basically creating a list of filing cabinets, allowing us to store two collections of data in one place. In this case, the data is a selection of image titles and image IDs.
Now create a new Java Class called CreateList and add this code:
What we have here is a method we can use to add new elements (setImage_title, setImage_ID) and retrieve them (getImage_title, getImage_ID). This will let us run through the two arrays we made and stick them into the ArrayList. You’ll need to import array lists.
We do this, like so:
So we’re performing a loop while we go through all the image titles and adding them to the correct array in the ArrayList one at a time. Each time, we’re using the same index (i), in order to add the image ID to its respective location.
Using the adapter
Before you head over to MyAdapter.java, you first need to create a new XML layout in the layout directory. I’ve called mine cell_layout.xml and it looks like so:
All this is, is the layout for the individual cells in our grid layout. Each one will have an image at the top, with text just underneath. Nice.
Now you can go back to your MyAdapter.java. This is where we’re going to take the list, take the cell layout and then use both those things to fill the RecyclerView. We already attached this to the RecyclerView in MainActivity.Java, so now all that’s left is… lots and lots of complex code.
It’s probably easiest if I just show you…
So what we’re doing here is to get our ArrayList and then create a ViewHolder. A ViewHolder makes it easier for us to iterate lots of views without having to write findViewByID every time – which would be impractical for a very long list.
We create the VewHolder by referencing the cell_layout file we created earlier, and then bind it with the data from our ArrayList. We find the TextView first and set that to be the relevant string, then we find the ImageView and use the image ID integer to set the image resource. Notice that I’ve also setScaleType to CENTER_CROP. This means that the image will be centered but cropped to fill the enter cell in a relatively attractive manner. There are other scale types but I believe that this is by far the most attractive for our purposes.
Don’t forget to import the ImageView and TextView classes. And remember you need to add some images to your drawables folder. Once you’ve done that though you should be ready to go!
Give it a try and you should end up with something that looks a little like this:
Источник
Как мы сделали свою библиотеку Android Gallery для просмотра медиаконтента
Привет, Хабр! Не так давно в поисках приключений, новых проектов и технологий я стал роботом устроился в Redmadrobot. Получил стул, монитор и макбук, а для разогрева — небольшой внутренний проект. Нужно было допилить и опубликовать самописную библиотеку для просмотра медиаконтента, которую мы используем в наших проектах. В статье я расскажу, как за неделю разобраться в touch events, стать опенсурсером, найти багу в Android sdk и опубликовать библиотеку.
Начало
Одна из важных фич наших приложений-магазинов — возможность просматривать видео и фото товаров и услуг вблизи и со всех сторон. Мы не хотели изобретать велосипед и отправились на поиски готовой библиотеки, которая бы нас устроила.
Планировали найти такое решение, чтобы пользователь мог:
- просматривать фотографии;
- масштабировать фото при помощи pinch to zoom и double tap;
- просматривать видео;
- листать медиаконтент;
- закрывать карточку с фото вертикальным свайпом (swipe to dismiss).
Вот, что мы нашли:
- FrescoImageViewer — поддерживает просмотр и пролистывание фото и основные жесты, однако не поддерживает просмотр видео и предназначен для библиотеки Fresco.
- PhotoView — поддерживает просмотр фото, большинство основных жестов управления, кроме пролистывания, swipe to dismiss, не поддерживает просмотр видео.
- PhotoDraweeView — аналогичная по функциональности PhotoView, но предназначена для Fresco.
Так как ни одна из найденных библиотек полностью не соотвествовала требованиям, нам пришлось написать свою.
Реализуем библиотеку
Чтобы получить нужную функциональность, мы доработали существующие решения из других библиотек. Тому, что получилось, решили дать скромное название Android Gallery.
Реализуем функциональность
Просмотр и масштабирование фотографий
Для просмотра фотографий взяли библиотеку PhotoView, которая из коробки поддерживает масштабирование.
Просмотр видео
Для просмотра видео взяли ExoPlayer, который переиспользутеся в MediaPagerAdapter. Когда пользователь открывает видео впервые, создаётся ExoPlayer. При переходе к другому элементу он ставится в очередь, так что при следующем запуске видео использоваться будет уже созданный экземпляр ExoPlayer. Это делает переход между элементами более плавным.
Перелистывание медиаконтента
Здесь мы использовали MultiTouchViewPager из FrescoImageViewer, который не перехватывает multi touch events, поэтому мы смогли добавить к нему жесты для масштабирования изображения.
Swipe to dismiss
В PhotoView не было поддержки swipe to dismiss и дебаунса (восстановления исходного размера картинки, когда картинка масштабируется в большую или меньшую сторону).
Вот как нам удалось с этим справиться.
Изучаем touch events для реализации swipe to dismiss
Прежде, чем перейти к поддержке swipe to dismiss, нужно разобраться, как работают touch events. Когда пользователь касается экрана, в текущей Activity вызывается метод dispatchTouchEvent(motionEvent: MotionEvent) , куда попадает MotionEvent.ACTION_DOWN . Этот метод решает дальнейшую судьбу события. Можно передать motionEvent в onTouchEvent(motionEvent: MotionEvent) на обработку касания или пустить дальше, сверху вниз по иерархии View. View, которая заинтересована в событии и/или в последующих событиях до ACTION_UP , возвращает true.
После все события текущего жеста (gesture) будут попадать в это View, пока жест не завершится событием ACTION_UP или родительский ViewGroup не перехватит управление (тогда во View придет событие ACTION_CANCELED ). Если событие обошло всю иерархию View и никого не заинтересовало, оно возвращается обратно в Activity в onTouchEvent(motionEvent: MotionEvent) .
В нашей библиотеке Android Gallery первое событие ACTION_DOWN доходит до dispatchTouchEvent() в PhotoView, где motionEvent передаётся в реализацию onTouch() , которая возвращает true. Дальше все события проходят такую же цепочку, пока не произойдёт одно из:
- ACTION_UP ;
- ViewPager попытается перехватить событие для пролистывания;
- VerticalDragLayout попытается перехватить событие для swipe to dismiss.
Перехват событий может осуществлять только ViewGroup в методе onInterceptTouchEvent(motionEvent: MotionEvent) . Даже если View заинтересована в каком-либо MotionEvent, само событие будет проходить через dispatchTouchEvent(motionEvent: MotionEvent) всей предшествующей цепочки ViewGroup. Соответственно родители всегда «наблюдают» за своими детьми. Любой родительский ViewGroup может перехватить событие и вернуть true в методе onInterceptTouchEvent(motionEvent: MotionEvent) , тогда все дочерние View получат MotionEvent.ACTION_CANCEL в onTouchEvent(motionEvent: MotionEvent) .
Пример: пользователь удерживает палец на некотором элементе в RecyclerView, тогда события обрабатываются в этом же элементе. Но как только он начнёт двигать пальцем вверх/вниз, RecyclerView перехватит события, и начнётся скролл, а View получит событие ACTION_CANCEL .
В Android Gallery VerticalDragLayout может перехватывать события для swipe to dismiss или ViewPager — для перелистывания. Но View может запретить родителю перехватывать события, вызвав метод requestDisallowInterceptTouchEvent(true) . Это может понадобиться, если View нужно совершить такие действия, перехват родителем которых для нас не желателен.
Например, когда пользователь в плеере проматывает трек к конкретному времени. Если бы родительский ViewPager перехватил горизонтальный скролл, произошёл бы переход к следующему треку.
Для обработки swipe to dismiss мы написали VerticalDragLayout, но он не получал touch событий от PhotoView. Чтобы понять почему так происходит, пришлось разобраться, как обрабатываются touch события в PhotoView.
- При MotionEvent.ACTION_DOWN в VerticalDragLayout срабатывает interceptTouchEvent() , который возвращает false, т.к. данный ViewGroup интересуют только вертикальные ACTION_MOVE. Направление ACTION_MOVE определяется в dispatchTouchEvent() , после чего событие передаётся в метод super.dispatchTouchEvent() во ViewGroup, где происходит передача события в реализацию interceptTouchEvent() в VerticalDragLayout.
Когда событие ACTION_DOWN доходит до метода onTouch() в PhotoView, то вьюха отбирает возможность перехватывать управление событиями. Все последующие события жеста не попадают в метод interceptTouchEvent() . Возможность перехватывать управление отдаётся родителю только в случае завершения жеста или если происходит горизонтальный ACTION_MOVE у правой/левой границы изображения.
Так как PhotoView разрешает родителю перехватывать управление только в случае горизонтального ACTION_MOVE , а swipe to dismiss — это вертикальный ACTION_MOVE , то VerticalDragLayout не может перехватить управление событиями для осуществления жеста. Для исправления нужно добавить возможность перехватывать управления в случае вертикального ACTION_MOVE .
Теперь в случае первого вертикального ACTION_MOVE PhotoView будет отдавать возможность перехвата родителю:
Следующий ACTION_MOVE будет перехвачен в VerticalDragLyout, при этом в дочерние View прилетит событие ACTION_CANCEL :
Все остальные ACTION_MOVE будут прилетать в VerticalDragLayout по стандартной цепочке. Важно, что после того как ViewGroup перехватывает управление событиями у дочернего View, дочерние View никак не могут вернуть себе управление.
Так мы реализовали поддержку swipe to dismiss для библиотеки PhotoView. В нашей библиотеке мы использовали вынесенные в отдельный модуль доработанные исходники PhotoView, а в оригинальный репозиторий PhotoView создали merge request.
Реализуем дебаунс в PhotoView
Напомним, что дебаунс — это анимация-восстановление допустимого масштаба, когда изображение масштабируется за его пределы.
В PhotoView такой возможности не было. Но раз уж мы начали копать чужой опенсорс, зачем останавливаться на достигнутом? В PhotoView можно задать ограничение на зум. Изначально это минимальный — х1 и максимальный — х3. За эти пределы изображение выйти не может.
Для начала мы решили убрать условие «запрет масштабирования по достижении минимума»: просто выкинули условие getScale() > mMinScale || scaleFactor > 1f . И тут внезапно…
Дебаунс заработал! Видимо, так произошло из-за того, что создатели библиотеки решили дважды подстраховаться, сделав и дебаунс, и ограничение на масштабирование. В реализации события onTouch, а именно в случае MotionEvent.ACTION_UP , если пользователь отмасштабировался больше/меньше максимума/минимума, запускается AnimatedZoomRunnable, который возвращает картинку к исходному размеру.
Также как и с swipe to dismiss, мы доработали PhotoView в исходниках нашей библиотеки и создали pull request с «добавлением» дебаунса в оригинальный PhotoView.
Исправляем внезапный баг в PhotoView
В PhotoView есть очень неприятный баг. Когда пользователь хочет увеличить изображение двойным тапом и у него случается приступ эпилепсии изображение начинает масштабироваться, оно может перевернуться на 180 градусов по вертикали. Этот баг можно встретить даже в популярных приложениях из Google Play, например, в ЦИАНе.
После долгого поиска мы всё-таки локализовали этот баг: иногда в матричное преобразование изображения для масштабирования на вход подаётся отрицательный scaleFactor, он-то и вызывает переворот изображения.
Для масштабирования из андроидовского ScaleGestureDetector достаём scaleFactor, который вычисляется следующим образом:
Если обложить данный метод дебаг-логами, можно отследить, при каких именно значениях переменных получается отрицательный scaleFactor:
Есть подозрение, что эту проблему пытались решить путём домножения spanDiff на SCALE_FACTOR == 0.5. Но это решение не поможет, если разница между mCurrSpan и mPrevSpan больше, чем в три раза. На этот баг уже даже заведён тикет, однако он до сих пор не исправлен.
Костыльное Самое простое решение этой проблемы — просто пропускать отрицательные значения scaleFactor. На практике пользователь не заметит, что изображение иногда зумируется чуть менее плавно, чем обычно.
Вместо заключения
Судьба пулл реквестов
Мы сделали локальное исправление и создали последний Pull Request в PhotoView. Несмотря на то, что некоторые PR висят там уже год, наши PR были добавлены в master-ветку и даже был выпущен новый релиз PhotoView. После чего мы решили выпилить локальный модуль из Android Gallery и подтянуть официальные исходники PhotoView. Для этого пришлось добавить поддержку AndroidX, который был добавлен в PhotoView в версии 2.1.3.
Где найти библиотеку
Исходный код библиотеки Android Gallery ищите тут — https://github.com/redmadrobot-spb/android-gallery, вместе с инструкцией по использованию. А для поддержки проектов, которые всё ещё используют Support Library, мы создали отдельную версию android-gallery-deprecated. Но будьте осторожны, ведь через год Support Library превратится в тыкву!
Что дальше
Сейчас библиотека полностью нас устраивает, но в процессе разработки возникли новые идеи. Вот некоторые из них:
- возможность использовать библиотеку в любой вёрстке, а не только отдельным FragmentDialog;
- возможность кастомизации UI;
- возможность подмены Gilde и ExoPlayer;
- возможность использовать что-то вместо ViewPager.
Ссылки
Пока писали статью, вышла похожая библиотека от разработчиков FrescoImageViewer. Они добавили поддержку transition animation, однако поддержка видео пока что есть только у нас. 🙂
Источник