Commit 976eb422 authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

photo screen

photo adapter
parent 21163719
......@@ -153,6 +153,9 @@ dependencies {
implementation "com.google.android.exoplayer:exoplayer-hls:$exoPlayerVersion"
implementation "com.google.android.exoplayer:extension-rtmp:$exoPlayerVersion"
//Photo view
implementation "com.github.chrisbanes:PhotoView:$photoViewVersion"
//Tests
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
......
package com.biganto.visual.roompark.domain.interactor
import com.biganto.visual.roompark.domain.model.AlbumSortedModel
import com.biganto.visual.roompark.domain.use_case.AlbumsUseCase
import io.reactivex.Observable
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 09.10.2019.
*/
class PhotoInteractor @Inject constructor(
private val useCase: AlbumsUseCase) {
fun fetchHeaderAlbums() = useCase.getProgressAlbums()
fun fetchAlbumPhotos(parentId: Int): Observable<List<AlbumSortedModel>> =
useCase.getChildAlbum(parentId)
.flatMap {
Observable.fromIterable(it).flatMap { alb ->
useCase.getPhotos(alb.albumId).take(1)
.map { photos ->
AlbumSortedModel(
alb.title,
alb.published,
alb.albumId,
photos
)
}
}.toList().toObservable()
}
.debounce(80L, TimeUnit.MILLISECONDS) // to reduce double list draw effect
}
......@@ -44,7 +44,16 @@ data class PhotoModel(
val sort:Int,
val resolutionList:List<PhotoResolutionModel>
)
){
fun optimalResolution(width:Int,height:Int) =
resolutionList
.asSequence()
.filter { it.resWidth >= width }
.filter { it.resHeight >= height }
.minBy { it.resHeight * it.resWidth }
?: resolutionList.maxBy { it.resHeight * it.resWidth }!!
}
data class PhotoResolutionModel(
val resName:String,
......
......@@ -2,6 +2,7 @@ package com.biganto.visual.roompark.presentation.screen.albums
import com.biganto.visual.roompark.conductor.BigantoBaseContract
import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.presentation.screen.photo.AlbumsScreenViewState
import io.reactivex.Observable
/**
......
......@@ -16,8 +16,10 @@ import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.conductor.BigantoBaseController
import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.presentation.screen.albums.util.AlbumsHeaderAdapter
import com.biganto.visual.roompark.presentation.screen.favorites.util.AlbumListAdapter
import com.biganto.visual.roompark.presentation.screen.photo.AlbumsScreenPresenter
import com.biganto.visual.roompark.presentation.screen.photo.AlbumsScreenViewState
import com.biganto.visual.roompark.presentation.screen.photo.util.AlbumsHeaderAdapter
import com.biganto.visual.roompark.presentation.screen.photo.util.AlbumListAdapter
import com.biganto.visual.roompark.util.extensions.scaleCenterCrop
import com.biganto.visual.roompark.util.view_utils.grid.CeilsDecoration
import com.google.android.material.textview.MaterialTextView
......@@ -45,7 +47,7 @@ class AlbumsScreenController :
constructor(args: Bundle):super(args)
constructor(id: Int) : super(bundleOf(SELECTED_ALBUM_INDEX_KEY to id))
constructor(id: Int) : super(bundleOf(com.biganto.visual.roompark.presentation.screen.photo.SELECTED_ALBUM_INDEX_KEY to id))
@BindView(R.id.headers_recycler_view)
......@@ -124,7 +126,7 @@ class AlbumsScreenController :
.factory()
.create(RoomParkApplication.component
,activity as RoomParkMainActivity
,args.getInt(SELECTED_ALBUM_INDEX_KEY))
,args.getInt(com.biganto.visual.roompark.presentation.screen.photo.SELECTED_ALBUM_INDEX_KEY))
.inject(this)
// @Inject
......
......@@ -27,7 +27,7 @@ interface AlbumsScreenComponent {
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
,@BindsInstance @Named(SELECTED_ALBUM_INDEX_KEY) camIndex:Int
,@BindsInstance @Named(com.biganto.visual.roompark.presentation.screen.photo.SELECTED_ALBUM_INDEX_KEY) camIndex:Int
): AlbumsScreenComponent
}
......
......@@ -16,7 +16,7 @@ import javax.inject.Named
class AlbumsScreenPresenter @Inject constructor(
private val interactor: AlbumsInteractor
,@Named(SELECTED_ALBUM_INDEX_KEY) private var selectedIndex:Int
,@Named(com.biganto.visual.roompark.presentation.screen.photo.SELECTED_ALBUM_INDEX_KEY) private var selectedIndex:Int
)
: BigantoBasePresenter<AlbumsScreen, AlbumsScreenViewState>() {
......@@ -46,7 +46,7 @@ class AlbumsScreenPresenter @Inject constructor(
val headerItemSelected = intent(AlbumsScreen::onAlbumSelected)
.doOnNext { selectedIndex = it.albumId }
.flatMap<AlbumsScreenViewState> {model ->
.flatMap<AlbumsScreenViewState> { model ->
requestAlbum(model.albumId)
.startWith(Observable.just(AlbumsScreenViewState.HeaderAlbumChoosed(item = model)))
}
......
......@@ -6,6 +6,7 @@ import android.widget.TextView
import butterknife.BindView
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.presentation.screen.photo.util.AlbumsHeaderViewHolder
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonRecyclerAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonViewHolder
import com.squareup.picasso.Picasso
......
......@@ -10,6 +10,9 @@ import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.domain.model.AlbumSortedModel
import com.biganto.visual.roompark.domain.model.PhotoModel
import com.biganto.visual.roompark.domain.model.PhotoResolutionModel
import com.biganto.visual.roompark.presentation.screen.photo.util.AlbumViewHolder
import com.biganto.visual.roompark.presentation.screen.photo.util.PhotosAdapter
import com.biganto.visual.roompark.presentation.screen.photo.util.PhotosViewHolder
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonRecyclerAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonViewHolder
import com.biganto.visual.roompark.util.view_utils.image_view.RoundedImageView
......@@ -65,7 +68,7 @@ class PhotosViewHolder(itemView: View) : CommonViewHolder<PhotoModel>(itemView)
}
override fun onViewBound(model: PhotoModel) {
model.resolutionList.lowelest()?.let {
com.biganto.visual.roompark.presentation.screen.photo.util.lowelest()?.let {
picassoAsync
.load(it.url)
.centerCrop()
......
......@@ -14,7 +14,7 @@ import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.domain.model.ArticlePreviewModel
import com.biganto.visual.roompark.domain.model.FeedModel
import com.biganto.visual.roompark.domain.model.WebCamModel
import com.biganto.visual.roompark.presentation.screen.albums.AlbumsScreenController
import com.biganto.visual.roompark.presentation.screen.photo.AlbumsScreenController
import com.biganto.visual.roompark.presentation.screen.article.ArticleScreenController
import com.biganto.visual.roompark.presentation.screen.feed_list.ArticlesScreenController
import com.biganto.visual.roompark.presentation.screen.feeds.utils.AlbumsPreviewAdapter
......
package com.biganto.visual.roompark.presentation.screen.photo
import com.biganto.visual.roompark.conductor.BigantoBaseContract
import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import io.reactivex.Observable
/**
* Created by Vladislav Bogdashkin on 30.09.2019.
*/
interface PhotoScreen : BigantoBaseContract<PhotoScreenViewState> {
fun onAlbumSelected(): Observable<AlbumPreviewModel>
}
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.photo
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.View
import androidx.core.os.bundleOf
import androidx.core.widget.NestedScrollView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.IBottomNavigation
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.conductor.BigantoBaseController
import com.biganto.visual.roompark.domain.model.AlbumPreviewModel
import com.biganto.visual.roompark.presentation.screen.photo.util.AlbumListAdapter
import com.biganto.visual.roompark.util.extensions.scaleCenterCrop
import com.biganto.visual.roompark.util.view_utils.grid.CeilsDecoration
import com.google.android.material.textview.MaterialTextView
import com.squareup.picasso.Picasso
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import jp.wasabeef.picasso.transformations.BlurTransformation
import jp.wasabeef.picasso.transformations.ColorFilterTransformation
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Created by Vladislav Bogdashkin on 30.09.2019.
*/
const val SELECTED_ALBUM_INDEX_KEY = "SELECTED_ALBUM_INDEX"
class PhotoScreenController :
BigantoBaseController<PhotoScreenViewState
, PhotoScreen
, PhotoScreenPresenter>
, PhotoScreen {
constructor(args: Bundle):super(args)
constructor(id: Int) : super(bundleOf(SELECTED_ALBUM_INDEX_KEY to id))
@BindView(R.id.headers_recycler_view)
lateinit var headersRecyclerView: RecyclerView
@BindView(R.id.albums_recycler_view)
lateinit var albumsRecyclerView: RecyclerView
@BindView(R.id.photo_albums_container)
lateinit var nestedScrollView: NestedScrollView
@BindView(R.id.header_album_title)
lateinit var currentAlbomTitle: MaterialTextView
private val photosBackgroundTarget = object : com.squareup.picasso.Target {
override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
// nestedScrollView.background = placeHolderDrawable
}
override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
Timber.e(e)
}
override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
bitmap?.scaleCenterCrop(nestedScrollView)?.let {
nestedScrollView.background = BitmapDrawable(activity?.resources, it)
}
}
}
private fun bindRecycler() {
headersRecyclerView.isNestedScrollingEnabled = false
headersRecyclerView.layoutManager =
LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false)
headersRecyclerView.adapter = PhotoHeaderAdapter()
headersRecyclerView.itemAnimator = null
if (headersRecyclerView.itemDecorationCount == 0)
headersRecyclerView.addItemDecoration(
CeilsDecoration(1
, resources?.getDimensionPixelSize(R.dimen.ceil_grid_padding))
)
albumsRecyclerView.isNestedScrollingEnabled = false
albumsRecyclerView.layoutManager =
LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
albumsRecyclerView.adapter = AlbumListAdapter()
albumsRecyclerView.itemAnimator = null
}
override fun onViewBound(v: View) {
toolBar.hideAll()
bottomNavigation.hide()
bindRecycler()
}
override fun injectDependencies() {
getComponent()
}
@Inject
override lateinit var injectedPresenter: PhotoScreenPresenter
// @Inject
// lateinit var snacky:ISnackBarProvider
lateinit var rpActivity: RoomParkMainActivity
fun getComponent() = DaggerPhotoScreenComponent.factory()
.create(RoomParkApplication.component
,activity as RoomParkMainActivity
,args.getInt(SELECTED_ALBUM_INDEX_KEY))
.inject(this)
// @Inject
// lateinit var ac: RoomParkMainActivity
@Inject
lateinit var bottomNavigation: IBottomNavigation
override fun render(viewState: PhotoScreenViewState) {
when(viewState){
is PhotoScreenViewState.Idle -> render(viewState)
is PhotoScreenViewState.PhotoSelected -> render(viewState)
is PhotoScreenViewState.PhotoListLoaded -> render(viewState)
is PhotoScreenViewState.HeaderAlbumChoosed -> render(viewState)
is PhotoScreenViewState.SomeError -> render(viewState)
}
}
private fun render(viewState: PhotoScreenViewState.Idle){
}
private fun render(viewState: PhotoScreenViewState.SomeError) =
showError(viewState.exception)
@Inject
lateinit var picassoAsync:Picasso
private fun render(viewState: PhotoScreenViewState.PhotoListLoaded) {
(headersRecyclerView.adapter as PhotoHeaderAdapter).setItems(
viewState.list.asSequence().sortedByDescending { it.published }.toList()
)
headersRecyclerView.let {
it.scrollToPosition(
(it.adapter as PhotoHeaderAdapter).getItemPosition(viewState.selectedAlbumId)
)
}
viewState.list.first { it.albumId == viewState.selectedAlbumId }.let {
currentAlbomTitle.text = it.title
picassoAsync
.load(it.previewUrl)
.transform(BlurTransformation(activity, 13, 2))
.transform(ColorFilterTransformation(0xCC000000.toInt()))
.into(photosBackgroundTarget)
}
}
private fun render(viewState: PhotoScreenViewState.HeaderAlbumChoosed) {
(headersRecyclerView.adapter as PhotoHeaderAdapter)
.getItemPosition(viewState.item.albumId).let {
headersRecyclerView.scrollToPosition(it)
}
currentAlbomTitle.text = viewState.item.title
picassoAsync
.load(viewState.item.previewUrl)
.transform(BlurTransformation(activity, 13, 2))
.transform(ColorFilterTransformation(0xCC000000.toInt()))
.into(photosBackgroundTarget)
}
private fun render(viewState: PhotoScreenViewState.PhotoSelected){
(albumsRecyclerView.adapter as AlbumListAdapter).setItems(
viewState.list.asSequence().sortedByDescending { it.published }.toList()
)
}
override fun onAlbumSelected(): Observable<AlbumPreviewModel>
= (headersRecyclerView.adapter as PhotoHeaderAdapter)
.onItemClicked
.debounce (300L,TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { (albumsRecyclerView.adapter as AlbumListAdapter).setItems(arrayListOf())}
override fun getLayoutId(): Int = R.layout.albums_screen
}
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.photo
import android.content.Context
import com.biganto.visual.roompark.base.IBottomNavigation
import com.biganto.visual.roompark.base.RoomParkMainActivity
import com.biganto.visual.roompark.di.dagger.AppComponent
import com.biganto.visual.roompark.di.dagger.PerScreen
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import javax.inject.Named
///**
// * Created by Vladislav Bogdashkin on 30.09.2019.
// */
@PerScreen
@Component(
modules = [PhotoScreenModule::class]
,dependencies = [AppComponent::class]
)
interface PhotoScreenComponent {
@Component.Factory
interface Factory{
fun create(
appComponent: AppComponent
,@BindsInstance activity: RoomParkMainActivity
,@BindsInstance @Named(SELECTED_ALBUM_INDEX_KEY) camIndex:Int
): PhotoScreenComponent
}
fun inject(controller: PhotoScreenController)
}
@Module
abstract class PhotoScreenModule{
@PerScreen
@Binds
abstract fun provideContext(activity: RoomParkMainActivity): Context
@PerScreen
@Binds
abstract fun provideBottomNavigation(activitiy: RoomParkMainActivity): IBottomNavigation
}
package com.biganto.visual.roompark.presentation.screen.photo
import com.biganto.visual.roompark.conductor.BigantoBasePresenter
import com.biganto.visual.roompark.domain.interactor.PhotoInteractor
import com.biganto.visual.roompark.util.monades.ExceptionString
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
import javax.inject.Named
/**
* Created by Vladislav Bogdashkin on 30.09.2019.
*/
class PhotoScreenPresenter @Inject constructor(
private val interactor: PhotoInteractor
,@Named(SELECTED_ALBUM_INDEX_KEY) private var selectedIndex:Int
)
: BigantoBasePresenter<PhotoScreen, PhotoScreenViewState>() {
override fun defaultErrorViewStateHandler() =
{e: ExceptionString -> PhotoScreenViewState.SomeError(e) }
override fun bindIntents() {
// val fetchSelected = requestAlbum(selectedIndex)
//
//
// val headerItemSelected = intent(PhotoScreen::onAlbumSelected)
// .doOnNext { selectedIndex = it.albumId }
//
// .flatMap<PhotoScreenViewState> { model ->
// requestAlbum(model.albumId)
// }
val state = restoreStateObservable
// .mergeWith(fetchParents)
// .mergeWith(fetchSelected)
// .mergeWith(headerItemSelected)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
subscribeViewState(state.cast(PhotoScreenViewState::class.java), PhotoScreen::render)
}
}
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.photo
import com.biganto.visual.roompark.conductor.BigantoBaseViewState
import com.biganto.visual.roompark.domain.model.PhotoModel
import com.biganto.visual.roompark.domain.model.PhotoResolutionModel
import com.biganto.visual.roompark.util.monades.ExceptionString
/**
* Created by Vladislav Bogdashkin on 30.09.2019.
*/
sealed class PhotoScreenViewState : BigantoBaseViewState() {
class Idle : PhotoScreenViewState()
class PhotoListLoaded(val list:List<PhotoModel>) : PhotoScreenViewState()
class PhotoSelected(val model:PhotoResolutionModel) : PhotoScreenViewState()
class SomeError(val exception: ExceptionString) : PhotoScreenViewState()
}
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.photo.util
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import androidx.recyclerview.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
import butterknife.BindView
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.domain.model.AlbumSortedModel
import com.biganto.visual.roompark.domain.model.PhotoModel
import com.biganto.visual.roompark.domain.model.PhotoResolutionModel
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonRecyclerAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonViewHolder
import com.biganto.visual.roompark.util.view_utils.image_view.RoundedImageView
import com.google.android.material.textview.MaterialTextView
/**
* Created by Vladislav Bogdashkin on 16.10.2019.
*/
class AlbumListAdapter : CommonRecyclerAdapter<AlbumViewHolder, AlbumSortedModel>() {
override val vhKlazz = AlbumViewHolder::class
override fun getVhLayout(): Int = R.layout.date_album_viewholder
}
class AlbumViewHolder(itemView: View) : CommonViewHolder<AlbumSortedModel>(itemView) {
@BindView(R.id.date_title_textview)
lateinit var albumTitle: MaterialTextView
@BindView(R.id.photos_recyclerview)
lateinit var photosRecyclerView: RecyclerView
// @BindView(R.id.camStatusIcon) lateinit var camStatusIcon:ImageView
override fun onViewBound(model: AlbumSortedModel) {
albumTitle.text = model.title
photosRecyclerView.isNestedScrollingEnabled = false
photosRecyclerView.layoutManager =
StaggeredGridLayoutManager(4, RecyclerView.VERTICAL)
(photosRecyclerView.layoutManager as StaggeredGridLayoutManager)
.gapStrategy = GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
val adapter = PhotosAdapter()
photosRecyclerView.adapter = adapter
photosRecyclerView.itemAnimator = null
adapter.setItems(model.items)
}
}
class PhotosAdapter : CommonRecyclerAdapter<PhotosViewHolder, PhotoModel>() {
override val vhKlazz = PhotosViewHolder::class
override fun getVhLayout(): Int = R.layout.photo_preview_viewholder
}
class PhotosViewHolder(itemView: View) : CommonViewHolder<PhotoModel>(itemView) {
@BindView(R.id.photo_preview_imageview) lateinit var photoPreview: RoundedImageView
private val picassoAsync by lazy {
return@lazy RoomParkApplication.component.providePicassoAsync()
}
override fun onViewBound(model: PhotoModel) {
model.resolutionList.lowelest()?.let {
picassoAsync
.load(it.url)
.centerCrop()
.fit()
.into(photoPreview)
}}
}
fun List<PhotoResolutionModel>.lowelest() =
this.minBy { it.resWidth * it.resHeight }
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.photo.util
import android.view.View
import butterknife.BindView
import com.biganto.visual.roompark.R
import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.domain.model.PhotoModel
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonRecyclerAdapter
import com.biganto.visual.roompark.presentation.screen.settings.util.CommonViewHolder
import com.github.chrisbanes.photoview.PhotoView
import com.google.android.material.textview.MaterialTextView
import com.squareup.picasso.Callback
import com.squareup.picasso.Picasso
import jp.wasabeef.picasso.transformations.BlurTransformation
import timber.log.Timber
/**
* Created by Vladislav Bogdashkin on 15.10.2019.
*/
class PhotoViewerAdapter : CommonRecyclerAdapter<PhotoViewerViewHolder, PhotoModel>() {
override val vhKlazz = PhotoViewerViewHolder::class
override fun getVhLayout(): Int = R.layout.photo_page_viewholder
fun getItemPosition(model: PhotoModel) = getItemPosition(model.photoId)
fun getItemPosition(modelId: Int) = list.indexOfFirst {
it.photoId == modelId
}
}
class PhotoViewerViewHolder(itemView: View) : CommonViewHolder<PhotoModel>(itemView) {
@BindView(R.id.photo_view) lateinit var photoView:PhotoView
@BindView(R.id.photo_description) lateinit var photoDesc:MaterialTextView
val picassoAsync: Picasso by lazy {
return@lazy RoomParkApplication.component.providePicassoAsync()
}
override fun onViewBound(model: PhotoModel) {
photoDesc.visibility = if (model.description.isNullOrBlank()) View.GONE else View.VISIBLE
model.description?.let{ photoDesc.text = it}
// articleDate.text = dateFormatter.format(model.published)
model.optimalResolution(photoView.width,photoView.height).let {
picassoAsync
.load(it.url)
.transform(BlurTransformation(itemView.context,13,2))
.into(photoView,object :Callback{
override fun onSuccess() {
picassoAsync.load(it.url).into(photoView)
}
override fun onError(e: Exception?) {
Timber.e(e)
}
})
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/photo_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/photo_description"
style="@style/Accent_Minor_TextView.Inverted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@color/colorOpacityBackground"
android:paddingStart="32dp"
android:paddingTop="16dp"
android:paddingEnd="32dp"
android:paddingBottom="16dp"
android:textAlignment="center" />
</FrameLayout>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment