I am new to Android TV apps development.
My issue is I have one or more of app's screens cut off from the right when open the side menu.
I tried going through Google's Android TV Documentation but couldn't find a clue on it.
Here you can see attached image.
Here is my code for show/hide side menu for my TV app:
private fun showSideMenu() {
val sideMenuWidth = mSideMenuView.width
mSideMenuAnimator = ObjectAnimator.ofFloat(mSideMenuView, "translationX", 0f)
mSideMenuAnimator?.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator?) {
mSideMenuAnimator = null
mSideMenuView.visibility = View.VISIBLE
hideMenuText()
mSideMenuView.requestFocus()
mContainer.clearFocus()
}
override fun onAnimationEnd(animation: Animator?) {
}
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
})
mSideMenuAnimator?.duration = SIDE_MENU_ANIMATION_DURATION_MILLIS
val containerAnimator = ObjectAnimator.ofFloat(mContainer, "translationX", sideMenuWidth * 1f)
containerAnimator.duration = SIDE_MENU_ANIMATION_DURATION_MILLIS
val animatorSet = AnimatorSet()
animatorSet.play(mSideMenuAnimator).with(containerAnimator)
animatorSet.start()
}
private fun hideSideMenu() {
val sideMenuWidth = mSideMenuView.width
mSideMenuAnimator = ObjectAnimator.ofFloat(mSideMenuView, "translationX", -sideMenuWidth * 1f)
mSideMenuAnimator?.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator?) {
mSideMenuView.clearFocus()
mContainer.requestFocus()
}
override fun onAnimationEnd(animation: Animator?) {
mSideMenuAnimator = null
mSideMenuView.visibility = View.INVISIBLE
// Give focus control to current fragment
mCurrentFragment?.onReceiveFocus()
showMenuText()
}
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
})
mSideMenuAnimator?.duration = SIDE_MENU_ANIMATION_DURATION_MILLIS
val containerAnimator = ObjectAnimator.ofFloat(mContainer, "translationX", 0f)
containerAnimator.duration = SIDE_MENU_ANIMATION_DURATION_MILLIS
val animatorSet = AnimatorSet()
animatorSet.play(mSideMenuAnimator).with(containerAnimator)
animatorSet.start()
}
Here is my design for the side menu:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<com.myapp.widget.SideMenuView
android:id="#+id/view_side_menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:translationX="-220dp"
android:visibility="invisible" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="#+id/text_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="25dp"
android:layout_marginTop="25dp"
android:drawablePadding="4dp"
android:background="#drawable/background_side_menu_indicator"
android:drawableEnd="#drawable/icon_side_menu"
android:focusable="false"
android:text="MENU"
app:primaryForegroundColor="false"
style="#style/Text.SideMenuIndicator"
tools:ignore="RelativeOverlap" />
I would appreciate any clue about this issue.
Related
android beginner here. any help is much appreciated .
I am working on an app that can do a simple experiment.
the app displays images and asks users to rate it from 1 to 10.
I have 1 activity and two layouts experiment begins.
I have two layouts merged under FrameLayout.
what I want to achieve is :
'====>start experiment >show first image for 10 seconds >change layout to ratings layout>after user selects rating>loopback with different image>finish() when imagelist is empty. '
here is what I tried
I have tried viewflipper but shownext() and showprevious() methods dont update values.
now i am trying using layout visibility ,showing and hiding the layouts
class Presenter : AppCompatActivity() {
//val currentTime = Calendar.getInstance().time
private lateinit var binding: ActivityPresenterBinding
lateinit var layout1:View
lateinit var layout2:View
val imgList=Constants.getImages()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_presenter)
binding = ActivityPresenterBinding.inflate(layoutInflater)
setContentView(binding.root)
layout1 = binding.imageView
layout2=binding.ratingView
startSlider()
}
private fun startSlider() {
Handler(Looper.getMainLooper()).apply {
//arr.shuffle()
var index = 0
var imageView = binding.imgPresenter
val runnable = object : Runnable {
override fun run() {
imageView.setImageResource(imgList[index].image)
layout1.visibility= View.VISIBLE
Log.i("number ","${index}")
postDelayed(this, 5000)
index++
layout1.visibility=View.GONE
layout2.visibility=View.VISIBLE
binding.sbSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
Toast.makeText(applicationContext, "$p1", Toast.LENGTH_LONG).show()
//save rating (1-10)
}
override fun onStartTrackingTouch(p0: SeekBar?) {
}
override fun onStopTrackingTouch(p0: SeekBar?) {
}
})
}
}
postDelayed(runnable, 1000)
}
}
here my layout file
<FrameLayout android:id="#+id/viewFliper"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/img_presenter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_bg"
/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/ratingView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="how do you feel about this image from one to 10"
android:orientation="vertical"
android:gravity="center"
android:layout_marginBottom="20dp"
/>
<SeekBar
android:id="#+id/sb_seekbar"
style="#style/Widget.AppCompat.SeekBar.Discrete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="10"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:progress="3"
android:stepSize="1" />
</LinearLayout>
this is not hiding the views after the second image
what am I doing wrong?
try this and apply viewbinding
lateinit var runnable: Runnable
private fun startSlider() {
Handler(Looper.getMainLooper()).apply {
var flag = 0
var index = 0
runnable = Runnable {
if (flag == 0) {
img_presenter.setImageResource(imgList[index])
layout1.visibility = View.VISIBLE
layout2.visibility = View.GONE
postDelayed(runnable, 5000)
flag = 1
index++
} else {
layout1.visibility = View.GONE
layout2.visibility = View.VISIBLE
sb_seekbar.setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {
// Toast.makeText(applicationContext, "$p1", Toast.LENGTH_LONG).show()
//save rating (1-10)
}
override fun onStartTrackingTouch(p0: SeekBar?) {
}
override fun onStopTrackingTouch(p0: SeekBar?) {
index++
flag = 0
postDelayed(runnable, 1000)
}
})
}
}
postDelayed(runnable, 1000)
}
}
I want to bounce effect when doing STATE_EXPANDED or STATE_COLLAPSED like above image
I want an animation with BounceInterpolator applied.
The code below is an example I made.
activity_main.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.button.MaterialButton
android:id="#+id/main_button"
android:layout_width="200dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:text="click me !!"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/main_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#color/design_default_color_primary"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_hideable="false"
app:behavior_peekHeight="50dp">
<TextView
android:id="#+id/main_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="10dp"
android:text="BottomSheetTitle!!"
android:textColor="#color/white"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
val mainButton: MaterialButton by lazy { findViewById(R.id.main_button) }
val bottomSheet: ConstraintLayout by lazy { findViewById(R.id.main_bottom_sheet) }
val bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout> by lazy { BottomSheetBehavior.from(bottomSheet) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainButton.setOnClickListener {
when (bottomSheetBehavior.state) {
(BottomSheetBehavior.STATE_COLLAPSED) -> {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
(BottomSheetBehavior.STATE_EXPANDED) -> {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
}
}
}
}
there is no bounce.
Is there any solution to apply bounce animation?
1----------------------------------1
I solved using below code
I don't think this solution good
Answer please anytime if you have better good solution
bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_SETTLING) {
bottomSheet.animate()
.translationY(0f)
}
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheet.animate()
.setDuration(200)
.translationY(20f)
}
if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
bottomSheet.animate()
.setDuration(200)
.translationY(-20f)
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
})
I'm struggling with glide loading custom item layout with groupie. Being particular - on item bind when I use glide to load images (images are ~12kb). Experiencing a "frozen frame". Profiler shows big CPU usage jump on the moment when glide loads images. It lags even if I add 3 items at a time but I need to add more (there is not much difference in cpu usage between 3 and 48 items so it freezes for about the same time). Tested with loading it from Resources/Precached/Direct download, with and without RequestOptions() - everything is the same
Some code related to the issue:
HomeScreen.kt
var subscription = 0
//Transformation
var loading = false
var itemWidth = 120
class HomeScreen : AppCompatActivity() {
val categoriesReference = FirebaseDatabase.getInstance().getReference("/categories")
var photos = ArrayList<PhotoItem>()
val adapter = GroupAdapter<ViewHolder>()
var itemsAllowed = 48
lateinit var manager: GridLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home_screen)
itemWidth = getItemWidth(this#HomeScreen)
setSupportActionBar(toolbar)
window.exitTransition = null
//Action bar options
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.title = "Все обои"
//FIX BLINKING ON TRANSITIONS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
/*var fade = Fade()
fade.excludeTarget(toolbar, true)
fade.excludeTarget(toolbar2, true)
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setEnterTransition(fade)
getWindow().setExitTransition(fade)*/
}
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_FULLSCREEN)
//Working with layout
manager = GridLayoutManager(this, calculateNoOfColumns(this#HomeScreen))
adapter.setHasStableIds(true)
recycleView.isNestedScrollingEnabled = false
recycleView.adapter = adapter
recycleView.layoutManager = manager
loadPageFully()
addItemsToMenu()
//Listener for navigation view
navigView.setNavigationItemSelectedListener {
/*if (it.toString() == "Купить тариф") {
subscription = 1
alert("Тариф успешно установлен") {
navigView.menu.clear()
addItemsToMenu()
loadPageFully()
yesButton { }
supportActionBar?.title = "Все обои"
}.show()
true
} else {*/
when(it.title.toString()){
"Все обои" -> {
loadPageFully()
}
}
drawerLayoutMain.closeDrawer(GravityCompat.START, true)
for (i in 0 until navigView.menu.size()) {
navigView.menu.getItem(i).setChecked(false)
}
it.setChecked(true)
loadPageFully(it.toString())
loadingImageView.visibility = View.GONE
waveImageView.visibility = View.GONE
supportActionBar?.title = it.toString()
true
// }
}
//Shuffle images in layout manager
shuffleButton.onClick {
photos.shuffle()
adapter.clear()
photos.forEach {
if (manager.itemCount < itemsAllowed) {
adapter.add(it)
}
}
}
scrollView3.setOnScrollChangeListener(object : View.OnScrollChangeListener {
override fun onScrollChange(v: View?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) {
println(loading.toString())
if (!loading && !scrollView3.canScrollVertically(1) && manager.itemCount > 0) {
loading = true
if (itemsAllowed == photos.size) {
} else if (itemsAllowed + 48 >= photos.size) {
for (i in 48.downTo(0)) {
if (itemsAllowed + i == photos.size) {
itemsAllowed += i
waveImageView.visibility = View.VISIBLE
doAsync {
Thread.sleep(300)
scrollView3.fullScroll(View.FOCUS_DOWN)
}
}
}
} else {
waveImageView.visibility = View.VISIBLE
doAsync {
Thread.sleep(300)
scrollView3.fullScroll(View.FOCUS_DOWN)
}
itemsAllowed += 48
}
checkOk()
}
}
})
recycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
SCROLL_STATE_IDLE -> Glide.with(this#HomeScreen).resumeRequests()
SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING -> Glide.with(this#HomeScreen).pauseRequests()
}
}
})
}
//On home button clicked
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
android.R.id.home -> {
drawerLayoutMain.openDrawer(GravityCompat.START)
true
}
else -> super.onOptionsItemSelected(item)
}
}
fun getEntries(categoryName: String = "All") {
//This fun with ChildEventListeners(Firebase SDK) gets urls and pushes them to photos no problem here because by the time lags occur they all are in photos.
}
var okToLoad = false
var loaded = 0
fun loadImages() {
if (okToLoad) {
okToLoad = false
if (photos.size < itemsAllowed) {
itemsAllowed = photos.size
}
loading = true
try {
for (i in manager.findLastVisibleItemPosition() + 1 until itemsAllowed) {
Glide.with(this#HomeScreen).load(photos[i].url).apply(
RequestOptions().diskCacheStrategy(
DiskCacheStrategy.ALL
)/*.skipMemoryCache(true)*/
).apply(RequestOptions().dontTransform()/*.override(220, 330)*/)
.listener(object : RequestListener<Drawable> {
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: com.bumptech.glide.request.target.Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
loaded += 1
println("1:$loaded")
println("2:$itemsAllowed")
if (loaded == itemsAllowed) {
Timer().schedule(object : TimerTask() {
override fun run() {
runOnUiThread {
waveImageView.visibility = View.GONE
shuffleButton.visibility = View.VISIBLE
okToLoad = true
loading = false
loadingImageView.visibility = View.GONE
for (i in manager.findLastVisibleItemPosition() + 1 until loaded) {
adapter.add(photos[i])
}
}
}
}, 1000)
}
return false
}
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: com.bumptech.glide.request.target.Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
return false
}
}).preload(220, 330)
}
} catch (e: IndexOutOfBoundsException) {
} catch (e: FileNotFoundException) {
Toast.makeText(this#HomeScreen, "Квота превышена", Toast.LENGTH_LONG).show()
}
}
}
fun checkOk() {
doAsync {
if (photos.size == 0) {
okToLoad = false
Thread.sleep(100)
checkOk()
} else {
okToLoad = true
uiThread {
loadImages()
}
}
}
}
fun addItemsToMenu() {
categoriesReference.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
navigView.menu.add(p0.key.toString()).setIcon(R.drawable.ic_bookmark_black_24dp)
}
override fun onCancelled(p0: DatabaseError) = Unit
override fun onChildChanged(p0: DataSnapshot, p1: String?) = Unit
override fun onChildMoved(p0: DataSnapshot, p1: String?) = Unit
override fun onChildRemoved(p0: DataSnapshot) = Unit
})
}
fun loadPageFully(key: String = "All") {
shuffleButton.visibility = View.GONE
loaded = 0
okToLoad = false
loadingImageView.visibility = View.VISIBLE
doAsync {
Glide.get(this#HomeScreen).clearDiskCache()
}
Glide.get(this#HomeScreen).clearMemory()
manager.removeAndRecycleAllViews(recycleView.Recycler())
adapter.clear()
photos.clear()
getEntries(key)
checkOk()
}
fun calculateNoOfColumns(context: Context): Int {
val displayMetrics = context.getResources().getDisplayMetrics();
val dpWidth = displayMetrics.widthPixels / displayMetrics.density;
val scalingFactor = 110 // You can vary the value held by the scalingFactor
// variable. The smaller it is the more no. of columns you can display, and the
// larger the value the less no. of columns will be calculated. It is the scaling
// factor to tweak to your needs.
var columnCount = (dpWidth / scalingFactor).toInt()
if (columnCount < 3) columnCount = 3
return columnCount // if column no. is less than 2, we still display 2 columns
}
fun getItemWidth(context: Context): Int {
val displayMetrics = context.resources.displayMetrics
val dpWidth = displayMetrics.widthPixels
return (dpWidth / calculateNoOfColumns(context)).toInt()
}
}
val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()
//Layout item for adapter
class PhotoItem(
val cnt: Context,
var url: String,
var downloads: Int,
var ref: DatabaseReference,
var locked: Boolean
) : Item<ViewHolder>() {
override fun bind(viewHolder: ViewHolder, position: Int) {
viewHolder.itemView.itemImageView.layoutParams.width = itemWidth
viewHolder.itemView.itemImageView.layoutParams.height = itemWidth
Glide.with(cnt)
.load(url)
.apply(RequestOptions().placeholder(R.drawable.placeholder).format(DecodeFormat.PREFER_RGB_565))/*transition(DrawableTransitionOptions.withCrossFade(factory)).apply(RequestOptions().placeholder(R.drawable.background))*//**//*.skipMemoryCache(true)*//**//*priority(Priority.HIGH))*//**//*.listener(
object : RequestListener<Bitmap> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: com.bumptech.glide.request.target.Target<Bitmap>?,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Bitmap?,
model: Any?,
target: Target<Bitmap>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false
}
})*/.into(viewHolder.itemView.itemImageView)
viewHolder.itemView.itemImageView.transitionName = url
viewHolder.itemView.setOnClickListener {
var intent = Intent(cnt, PhotoScreen::class.java).apply {
putExtra("url", url)
putExtra("ref", ref.toString())
putExtra("downs", downloads.toString())
putExtra("locked", locked.toString())
}
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
cnt as AppCompatActivity,
viewHolder.itemView.itemImageView,
viewHolder.itemView.itemImageView.transitionName
)
cnt.startActivity(intent, options.toBundle())
}
}
override fun getLayout(): Int {
return R.layout.image_item
}
Home
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawerLayoutMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeScreen"
android:background="#drawable/background">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:id="#+id/relativeLayout"
>
<androidx.core.widget.NestedScrollView
android:layout_width="0dp"
android:layout_height="0dp"
tools:layout_conversion_absoluteHeight="598dp"
tools:layout_conversion_absoluteWidth="384dp" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="#+id/toolbar"
android:id="#+id/scrollView3"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintBottom_toBottomOf="parent"
android:overScrollMode="ifContentScrolls"
android:fillViewport="false">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:id="#+id/linearLayout"
android:descendantFocusability="blocksDescendants"
android:layout_marginLeft="0dp" android:layout_marginRight="0dp"
android:layout_gravity="fill">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="wrap_content"
android:layout_height="-100dp"
android:layout_marginTop="0dp" android:id="#+id/recycleView"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_marginRight="0dp"
android:focusableInTouchMode="true"
android:scrollbars="vertical"
android:layout_gravity="center_horizontal|fill"
>
</androidx.recyclerview.widget.RecyclerView>
<pl.droidsonroids.gif.GifImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/waveImageView" android:src="#drawable/intro_image"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone" android:background="#drawable/background"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<androidx.appcompat.widget.Toolbar android:id="#+id/toolbar" android:layout_width="0dp"
android:layout_height="45dp"
android:background="#drawable/background"
app:titleTextColor="#android:color/white"
android:textAlignment="gravity"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:isScrollContainer="false"
android:fitsSystemWindows="false"
tools:layout_conversion_absoluteHeight="42dp"
tools:layout_conversion_absoluteWidth="384dp"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="#drawable/ic_list_black_24dp">
</androidx.appcompat.widget.Toolbar>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" app:srcCompat="#drawable/ic_shuffle_black_24dp"
android:id="#+id/shuffleButton"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="#+id/toolbar"
android:padding="10dp"
app:layout_constraintBottom_toTopOf="#+id/scrollView3"
android:focusedByDefault="true"
/>
<pl.droidsonroids.gif.GifImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/loadingImageView" android:visibility="visible"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent" android:src="#drawable/placeholder_gif"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="#layout/header_menu"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar" android:id="#+id/navigView"
app:itemBackground="#drawable/menu_item_background"
android:background="#drawable/menu_bitmap"/>
</androidx.drawerlayout.widget.DrawerLayout>
ImageItem layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ImageView
android:layout_width="110dp"
android:layout_height="110dp"
android:id="#+id/itemImageView"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
android:scaleType="centerCrop"/>
</FrameLayout>
OnMeasure() and nativePollOnce() methods takes a lot as I see. Still, don't know what to do with it
Solved it
The main problem was that groupie adapter as for now makes diffs on UI thread. And that caused lags. Switching to another library may help you. I've switched to Epoxy by airbnb
P.S. I had nested recyclerview in scrollview. Removing scrollview added more performance
I've been trying for a week loading items inside some horizontal recyclerviews contained in one vertical recyclerview(parent). Both need to have endless scrolling the horizontal ones to the right and the vertical while descending.
Right now the endless scrolling and the loading works. However, this is not working correctly because the horizontal ones are loading crazy data from the other children (My guess is that I'm creating the presenters inside the ViewHolder and some how data gets mixed up)
Is there any other way of doing this. That could work better and faster than my approach. Also, some this I can notice the vertical loading is not as smooth as I've seen in some other apps.
Any help would be greatly appreciated
class EventCatalogAdapter(private val presenter: EventCatalogPresenter):
RecyclerView.Adapter<EventCatalogAdapter.ViewHolder>() {
override fun getItemCount() = presenter.getCategoryEventCount()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
EventCatalogAdapter.ViewHolder(parent.inflate(R.layout.item_event_catalog), viewType)
override fun onBindViewHolder(holder: ViewHolder, position: Int) =
presenter.bind(holder, position)
override fun getItemViewType(position: Int) = presenter.getItemType(position)
class ViewHolder(itemView: View, type: Int): RecyclerView.ViewHolder(itemView),
EventCatalogItemView, EventViewList, OnItemClickListener<Event>,
EndlessScrollListener.DataLoader {
private lateinit var companyId: String
private lateinit var catId: String
private val eventPresenter = EventPresenterImpl(this)
override val titleFormat: String? get() = ""
init {
val layoutManager = HorizontalLinearLayoutManager(itemView.context,
LinearLayoutManager.HORIZONTAL, false)
itemView.rvEvents.layoutManager = layoutManager
itemView.rvEvents.addItemDecoration(HorizontalItemDecorator(itemView.context))
itemView.rvEvents.adapter = EventAdapter(eventPresenter, type, this)
itemView.rvEvents.addOnScrollListener(EndlessScrollListener(layoutManager, this))
}
override fun setTitleVisibility(b: Boolean) {
itemView.tvCategoryTitle.visibility = if(!b) View.GONE else View.VISIBLE
}
override fun setCategoryTitle(title: String) {
itemView.tvCategoryTitle.text = title
}
override fun eventsByCategory(companyId: String, categoryId: String) {
this.companyId = companyId
catId = categoryId
eventPresenter.getEventsByCategories(companyId, categoryId)
}
override fun loadMoreData(totalItems: Int) {
Log.d("LOAD", "Event presenter: $eventPresenter for category: $catId")
eventPresenter.getMoreEventsByCategories(companyId, catId, totalItems)
}
override fun setActiveEvent(event: Event?) {}
override fun showSettings() {}
override fun sendMessage(action: String, bundle: Bundle?) {}
override fun showStatus(status: Int) {}
override fun refresh() {
itemView.rvEvents.adapter.notifyDataSetChanged()
}
override fun showMessageTemplate(code: Int) {}
override fun hideMessageTemplate() {
refresh()
}
override fun onItemClick(item: Event) {
}
}
}
class EventAdapter(private val presenter: EventPresenter,
private val listener: OnItemClickListener<Event>):
RecyclerView.Adapter<EventAdapter.ViewHolderItemView>() {
private var type: Int = EVENT_STANDARD
constructor(presenter: EventPresenter, type: Int, listener: OnItemClickListener<Event>) :
this(presenter, listener) {
this.type = type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolderItemView(parent.inflate(type))
override fun onBindViewHolder(holder: ViewHolderItemView, position: Int) =
presenter.bind(holder, position)
override fun getItemCount() = presenter.getCount()
class ViewHolderItemView(itemView: View) : RecyclerView.ViewHolder(itemView), EventItemView {
override fun setEventDate(date: Date?) {
itemView.tvDate.text = Tools.formatDate(itemView.context, date)
}
override fun setName(name: String) {
itemView.tvName.text = name
}
override fun setScannerVisibility(scannerVisibility: Boolean) {
itemView.ibScanner.visibility = if (scannerVisibility) View.VISIBLE else View.INVISIBLE
}
override fun setEventPoster(posterUrl: String, transformation: Transformation?) {
Tools.loadImage(posterUrl, itemView.ivPoster, transformation, R.mipmap.portrait_test)
}
override fun setTotalRegistrants(totalRegistrants: Long) {
itemView.tvQtyRegs.text = totalRegistrants.toString()
}
override fun addScanAction(event: Event) {
itemView.ibScanner.setOnClickListener {
val auth = FirebaseAuth.getInstance()
val prefs = PreferenceHelper.customPrefs(itemView.context, auth.currentUser!!.uid)
prefs.edit().putString(EventInteractorImpl.FIELD_EVENT_ID, event.eventId).apply()
(itemView.context as SettingsActivity).launchScanner()
}
}
override fun addEventAction(event: Event) {
itemView.setOnClickListener {
(itemView.context as SettingsActivity).showRegistrants(event)
}
}
}
}
// Parent items:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="220dp">
<TextView
android:id="#+id/tvCategoryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/vertical_spacing"
android:paddingBottom="#dimen/vertical_spacing"
android:layout_marginEnd="#dimen/horizontal_spacing"
android:layout_marginStart="#dimen/horizontal_spacing"
android:textAlignment="center"
android:textSize="18sp"
android:textAllCaps="true"
android:fontFamily="sans-serif-light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/txt_cat_title" />
<android.support.v7.widget.RecyclerView
android:id="#+id/rvEvents"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="#dimen/vertical_spacing"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvCategoryTitle" />
</android.support.constraint.ConstraintLayout>
// Children items
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="110dp"
android:layout_height="match_parent"
android:stateListAnimator="#animator/tile_elevation">
<ImageView
android:id="#+id/ivPoster"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/txt_event_image"
android:src="#mipmap/portrait_test"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tvName"
style="#style/SubTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textIsSelectable="false"
android:textAllCaps="true"
app:layout_constraintBottom_toTopOf="#+id/ibRegistrants"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="Washington D.C. " />
<TextView
android:id="#+id/tvDate"
style="#style/SubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="#+id/tvName"
app:layout_constraintStart_toStartOf="#+id/ibRegistrants"
tools:text="03/23" />
<TextView
android:id="#+id/tvQtyRegs"
style="#style/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textIsSelectable="false"
app:layout_constraintBottom_toBottomOf="#+id/ibRegistrants"
app:layout_constraintStart_toEndOf="#+id/ibRegistrants"
app:layout_constraintTop_toTopOf="#+id/ibRegistrants"
tools:text="140" />
<ImageView
android:id="#+id/ibRegistrants"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:contentDescription="#string/txt_registrants"
android:src="#drawable/ic_registrants_48px"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="#+id/ibScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="#string/txt_registrants"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_scan_action" />
</android.support.constraint.ConstraintLayout>
I'm trying to build Android app in Kotlin using dataBinding and when I'm trying to compile this code
#Bindable
var progress:Int=1
#NotNull
#InverseBindingAdapter(attribute = "progress")
fun SeekBar.getProgress():Int{
return this.progress
}
#BindingAdapter(value = ["progressAttrChanged"])
fun setListeners(seekBar: SeekBar,inverseBindingListener: InverseBindingListener){
var listener=object: SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, Progress: Int, fromUser: Boolean) {
progress=Progress
mBeatBox.mRange=progress/66.67 as Float
inverseBindingListener.onChange()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
}
seekBar.setOnSeekBarChangeListener(listener)
}
I get this errors https://ibb.co/cBHRwx.
Here is view of modelView
<data>
<variable
name="viewModel"
type="com.bignerdranch.android.beatboxkotlin.Models.BeatBoxViewModel"/>
</data>
....
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:text="progres"
android:gravity="center"
android:layout_weight="9"/>
<android.support.v7.widget.AppCompatSeekBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="9"
app:progress="#={viewModel.progress}"/>
How can I solve this?
I solved this error in this way
Updated viewModel
`
var progress:Int=mBeatBox.mRange.toInt()
#Bindable set(value){field=value;notifyChange()}
#Bindable get()=field
fun getEditListener():SeekBar.OnSeekBarChangeListener{
var listener=object: SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, Progress: Int, fromUser: Boolean) {
progress=Progress
mBeatBox.mRange=(progress/66.67).toFloat()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
}
return listener
}`
And View looks like:
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:text="progress"
android:gravity="center"
android:layout_weight="9"
android:text="#{String.valueOf(viewModel.progress)}"/>
<android.support.v7.widget.AppCompatSeekBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="9"
app:OnSeekBarChangeListener="#{viewModel.EditListener}"/>
Not sure exactly what your error is pointing to, but looks like there's already a SeekbarBindingAdapter defined in the library, so you probably don't need to define your own. If you want to update things when progress changes, override set or use ObservableInt with an OnPropertyChangedCallback