I am using a constraint layout for my recycler view items. To animate (expand/collapse) them I use Constraint Set animation. The opening animation runs fine on all items. The closing animation runs fine also, but when the closing animation starts on item that is not the last all items jump up when animation starts, and not at the end of the animation.
Animation is performed on item click:
itemView.setOnClickListener {
val smallItemConstraint = ConstraintSet()
smallItemConstraint.clone(itemView.context, R.layout.day_of_week_small)
val largeItemConstraint = ConstraintSet()
largeItemConstraint.clone(itemView.context, R.layout.day_of_week)
val constraintToApply = if (isViewExpanded) smallItemConstraint else
largeItemConstraint
animateItemView(constraintToApply, itemView.dayOfWeekConstraintLayout)
if (!isViewExpanded) {
itemView.dayOfWeekWeatherIcon.visibility = View.VISIBLE
} else {
itemView.dayOfWeekWeatherIcon.visibility = View.GONE
}
isViewExpanded = !isViewExpanded
}
Where animateItemView is:
private fun animateItemView(constraintToApply: ConstraintSet,
constraintLayout: ConstraintLayout) {
TransitionManager.beginDelayedTransition(constraintLayout)
constraintToApply.applyTo(constraintLayout)
}
day_of_week.xml (expanded) layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="#+id/dayOfWeekConstraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/dayOfWeekWeatherIcon"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:contentDescription="#string/weather_image"
app:layout_constraintBottom_toBottomOf="#+id/dayOfWeekHumidityLabel"
app:layout_constraintEnd_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#tools:sample/avatars" />
<TextView
android:id="#+id/dayOfWeekText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/today"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/dayOfWeekItemVerticalGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="192dp" />
<TextView
android:id="#+id/dayOfWeekCurrentTemperatureText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAllCaps="true"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekText" />
<TextView
android:id="#+id/dayOfWeekDegreeCelsiusSign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/degree_celsius"
android:textAllCaps="true"
android:textSize="24sp"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekCurrentTemperatureText"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekText" />
<TextView
android:id="#+id/dayOfWeekWeatherStateText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/weather_state_text"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekDegreeCelsiusSign" />
<TextView
android:id="#+id/dayOfWeekWindLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/wind_label"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekHumidityLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="#string/humidityLabel"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWindLabel" />
<TextView
android:id="#+id/dayOfWeekWindDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindLabel"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekWindSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindDirection"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekWindSpeedLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/wind_speed"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindSpeed"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekHumidityText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekHumidityLabel"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWindSpeedLabel" />
<TextView
android:id="#+id/dayOfWeekHumidityPercentageLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="#string/percentage_sign"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekHumidityText"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWindSpeedLabel" />
</androidx.constraintlayout.widget.ConstraintLayout>
And day_of_week_small.xml (collapsed) layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="#+id/dayOfWeekConstraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/dayOfWeekWeatherIcon"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:contentDescription="#string/weather_image"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="#+id/dayOfWeekHumidityLabel"
app:layout_constraintEnd_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#tools:sample/avatars" />
<TextView
android:id="#+id/dayOfWeekText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/today"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/dayOfWeekItemVerticalGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="192dp" />
<TextView
android:id="#+id/dayOfWeekCurrentTemperatureText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAllCaps="true"
android:textSize="40sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekText" />
<TextView
android:id="#+id/dayOfWeekDegreeCelsiusSign"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/degree_celsius"
android:textAllCaps="true"
android:textSize="40sp"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekCurrentTemperatureText"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekText" />
<TextView
android:id="#+id/dayOfWeekWeatherStateText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/weather_state_text"
android:textSize="24sp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekDegreeCelsiusSign" />
<TextView
android:id="#+id/dayOfWeekWindLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/wind_label"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekHumidityLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="#string/humidityLabel"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toStartOf="#+id/dayOfWeekItemVerticalGuideline"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWindLabel" />
<TextView
android:id="#+id/dayOfWeekWindDirection"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindLabel"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekWindSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindDirection"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekWindSpeedLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/wind_speed"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekWindSpeed"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWeatherStateText" />
<TextView
android:id="#+id/dayOfWeekHumidityText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekHumidityLabel"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekWindSpeedLabel" />
<TextView
android:id="#+id/dayOfWeekHumidityPercentageLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="#string/percentage_sign"
android:textAllCaps="true"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toEndOf="#+id/dayOfWeekCurrentTemperatureText"
app:layout_constraintTop_toBottomOf="#+id/dayOfWeekCurrentTemperatureText" />
</androidx.constraintlayout.widget.ConstraintLayout>
What is the issue here and how do I fix it?
Thank you.
Animation example:
Before we get to how I was able to get everything to work, let's take a look at what is causing the behavior in your gif.
The reason why the other item views jumps up is because animations are purely visual. That is, the collapse animation doesn't actually animate the height of your item from a layout perspective, it only animates how the item is drawn. This is done for performance reasons (imagine having to re-layout all of the views 60 times a second). That is why when your item is collapsed all of the other views jump to the end layout position.
RecyclerViews are very good at animating the heights of their children and this is what we will use to solve the entire animation problem. I outline the complete solution below.
Preview GIF: https://giphy.com/gifs/SVlBnpeW3wIwNIVpVU
I was able to get ConstraintLayout + ConstrainSet + RecyclerViews working after some experimentation. I will share how I got it working.
The code
Here is a quick preview of the code.
private inner class MatchInfoAdapter (
private val context: Context
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items = listOf<MatchItem>()
private val inflater: LayoutInflater = LayoutInflater.from(context)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder =
FullViewHolder(inflater.inflate(viewType, parent, false))
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val item = items[position]
val h = holder as FullViewHolder
if (!item.isExpanded) {
h.collapsedConstraintSet.applyTo(h.rootView)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
val h = holder as FullViewHolder
val isExpanded = item.isExpanded
val constraint = if (isExpanded)
h.expandedConstraintSet
else
h.collapsedConstraintSet
constraint.applyTo(h.rootView)
bindGeneralViews(h, item, isExpanded)
if (isExpanded) {
bindExpandedExtraViews(h, item)
}
h.clickView.setOnClickListener {
toggleExpanded(h)
}
}
private fun bindGeneralViews(
h: FullViewHolder,
item: MatchItem,
isExpanded: Boolean
) {
// bind views that are visible when expanded and collapsed
}
private funbindExpandedExtraViews(
h: FullViewHolder,
item: MatchItem
) {
// bind views that are only shown when the item is expanded
}
private fun toggleExpanded(
h: FullViewHolder
) {
if (h.adapterPosition< 0) return // touch event can technically fire after a view is unbound
val autoTransition = AutoTransition()
val item = items[position]
item.isExpanded = !item.isExpanded
bindGeneralViews(h, item, newIsExpanded)
if (item.isExpanded) {
bindExpandedExtraViews(h, item)
autoTransition.ordering = AutoTransition.ORDERING_TOGETHER
autoTransition.duration = ANIMATION_DURATION_MS
TransitionManager.beginDelayedTransition(h.rootView, autoTransition)
h.expandedConstraintSet.applyTo(h.rootView)
notifyItemChanged(h.adapterPosition, Unit)
} else {
autoTransition.ordering = AutoTransition.ORDERING_TOGETHER
autoTransition.duration = ANIMATION_DURATION_MS
TransitionManager.beginDelayedTransition((h.rootView.parent as ViewGroup), autoTransition)
notifyItemChanged(h.adapterPosition, Unit)
}
}
}
data class MatchItem(
...
) {
// Exclude this field from equals/hachcode by declaring it in class body
var isExpanded: Boolean = false
}
private class FullViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView) {
...
val collapsedConstraintSet: ConstraintSet = ConstraintSet()
val expandedConstraintSet: ConstraintSet = ConstraintSet()
init {
collapsedConstraintSet.clone(rootView)
expandedConstraintSet.clone(rootView.context, R.layout.build_full_item)
}
}
How does it work?
The code relies heavily on notifyItemChanged(Int, Payload) and TransitionManager.beginDelayedTransition(). Let’s go over how these work first.
First, notifyItemChanged(Int, Payload) will ensure that the view holder passed to onBindViewHolder(RecyclerView.ViewHolder, Int, MutableList<Any>) is the same view holder as the view holder that is currently bound. Eg. let’s say A is the view holder currently bound to item 0. If we call notifyItemChanged(0, Unit) then we can guarantee that A will be passed to onBindViewHolder(RecyclerView.ViewHolder, Int, MutableList<Any>). In addition to this, RecyclerViews are very good at animating changes in item view height so notifyItemChanged() will notify the RecyclerView to check if the height changed and if it has to play a nice animation that will either animate other items up or down.
Second, TransitionManager.beginDelayedTransition() takes a snapshot of the current state of the view passed in. Then when ConstraintSet.applyTo() is called, the difference between the saved state and the current state is calculated and animations are applied to transition between the two, automatically.
Now that the basics are out of the way. Here is how expanding and collapsing items work.
For expanding an item:
User taps on the items.
toggleExpanded() is called.
Item’s view state is updated to expanded.
We pre-bind all of the views to the view holder so that no flickering occurs during animation and all views are fully bound.
TransitionManager.beginDelayedTransition() is called to take a snapshot of the item view state.
ConstraintSet.applyTo() is called to apply our expanded layout to the view and to animate the changes.
notifyItemChanged(h.``adapterPosition``, Unit) is called. This guarantees that when onBindViewHolder is called, we will get our fully bound view holder passed to us. In addition, it notifies the recyclerview that the height of the item has changed will let the recyclerview handle animating the height change.
For collapsing an item:
User taps on the items.
toggleExpanded() is called.
Item’s view state is updated to collapsed.
TransitionManager.beginDelayedTransition() is called to take a snapshot of the item view state.
notifyItemChanged(h.``adapterPosition``, Unit) is called. This guarantees that when onBindViewHolder is called, we will get our fully bound view holder passed to us. In addition, it notifies the recyclerview that the height of the item has changed will let the recyclerview handle animating the height change.
ConstraintSet.applyTo() is called to apply our collapsed layout to the view and to animate the changes.
Additional fun facts
Collapsing an item is actually way more complicated than meets the eye. The TransitionManager.beginDelayedTransition() call before notifyItemChanged(h.adapterPosition, Unit) is crucial. This is because the view holder passed to onBindViewHolder is always unbound due to how recyclerviews are implemented.
Why is this an issue? Well it means that if we were to call TransitionManager.beginDelayedTransition() in onBindViewHolder instead, the state it will save is that the view is unbound. When ConstraintSet.applyTo() is called, it will animate between an unbound view to a bound view and the default animation for this is to fade the view in. This is not what we want and the animation looks very ugly.
Related
I'm developing an app. In this app there's an alertdialog that opens when you click a point.
In this alert dialog there are two edittext, a gridview and a button. The gridview has associated a BaseAdapter that convert Uris to IconView (a class that extends ImageView) using Glide.
I want to set a maximum height of 350dp, but if there is just one row (there'll be always one image) I want the gridview to adapt.
I tried this way: How to have a GridView that adapts its height when items are added
But it doesn't work. So what I've tried is to count the number of elements and if there's is more than 2, set 350dp of height if there is less than 2, set 175dp. But it doesnt work.
The alert dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/puntoName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:fontFamily="sans-serif"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/puntoDescripcion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:maxHeight="100dp"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/puntoName" />
<GridView
android:id="#+id/photo_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:horizontalSpacing="6dp"
android:numColumns="2"
android:verticalSpacing="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/puntoDescripcion"></GridView>
<Button
android:id="#+id/cerrar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cerrar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/photo_grid" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is the code I'm using in order to change GridView height:
fun checkSize() {
val popUp: View = fragmentCaller.layoutInflater.inflate(R.layout.popup,null)
var photogrid: GridView = popUp.findViewById(R.id.photo_grid)
var params: ViewGroup.LayoutParams = photogrid.layoutParams
var tam: Int = 175
if (adapter.getDataSource().size>2) {
tam = DpToPixels(350)
}
photogrid.layoutParams.height = tam
photogrid.requestLayout()
}
private fun DpToPixels(dp: Int) : Int {
val escala: Float = fragmentCaller.requireContext().resources.displayMetrics.density;
var tam: Int = (dp * escala + 0.5f).toInt()
return tam
}
The size of the gridView is always 0. I don't know why. I just want to set android:layout_height programmatically. I call checkSize() method after I call Dialog.show() and everytime I do changes in the elements of adapter.
Thanks.
This has been asked before but those solutions didn't help me out. I have a RecyclerView that is scrollable and only partially visible on the screen at any one time. It holds a list of RadioButtons and some text related to each item. When you click on a RadioButton, the last button selected prior to this one should turn off. It does not. Each button you click gets selected and the other ones that you selected before stay selected as well.
It's set up like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
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"
android:background="#color/white"
tools:context=".ui.SettingsActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/video_settings"
android:textColor="#color/black"
android:textStyle="bold"
android:textSize="#dimen/clickableTextSize"
android:layout_alignParentStart="true"
/>
</RelativeLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="100"
android:gravity="bottom"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="15"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:text="#string/set_resolution_and_fps"
android:textSize="#dimen/history_text_size"
android:gravity="center_vertical"
android:background="#color/grey_200"
android:textColor="#color/black"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_video_resolution"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="80"
android:layout_marginTop="5dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/submitVideoSettings"
android:text="#string/submit"
android:layout_marginBottom="5dp"
android:textColor="#color/black"
android:layout_gravity="center|bottom"
/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
The recycler looks like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:orientation="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/resolutionRVRadioButton"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/resolutionRVTV"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="#string/at_symbol"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/framesPerSecondRVTV"
android:layout_marginStart="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/fps"
/>
</androidx.appcompat.widget.LinearLayoutCompat>
The code for the adapter looks like this.
class ResolutionRVAdapter(private val sharedNavAndMenuViewModel: SharedNavAndMenuViewModel,
private val lastResolutionSelected: String,
private val capabilityDataList: List<CameraCapabilitySpecificFPS>) : RecyclerView.Adapter<CameraResolutionRVAdapter.ViewHolder>(){
private var lastCheckedPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CameraResolutionRVAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.camera_resolution_recycler_item, parent, false)
val viewHolder = ViewHolder(v)
Timber.i("Added viewHolder: ${viewHolder.resolutionText.text}")
return ViewHolder(v)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val resolutionRadioButton : RadioButton = itemView.findViewById(R.id.resolutionRVRadioButton)
val resolutionText : TextView = itemView.findViewById(R.id.resolutionRVTV)
val framesPerSecondText : TextView = itemView.findViewById(R.id.framesPerSecondRVTV)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val child = capabilityDataList[position]
Timber.i("onBindViewHolder currently working with resolution ${child.resolution} range of $child.range")
holder.resolutionText.text = child.resolution.toString()
holder.framesPerSecondText.text = child.fps
Timber.d("OnBindViewholder and lastResolutionSelected = $lastResolutionSelected")
if(lastResolutionSelected.isEmpty()) {
if(position == 0) {
holder.resolutionRadioButton.isChecked = true
Timber.i("No last resolution was selected. Setting index 0 to true")
}
} else if(lastResolutionSelected == holder.resolutionText.text.toString()) {
Timber.i("An old resolution of ${holder.resolutionText.text} was selected. Setting index $position to true")
holder.resolutionRadioButton.isChecked = true
}
holder.resolutionRadioButton.setOnClickListener {
Timber.i("Index hit was: $position resolution at that index was: ${holder.resolutionText.text}")
if(holder.resolutionRadioButton.isChecked) {
//Button was already checked...must be the same button hit before...nothing to do
} else {
holder.resolutionRadioButton.isChecked = true
runOnUiThread {
notifyItemChanged(lastCheckedPosition)
notifyItemChanged(position)
}
lastCheckedPosition = position
}
}
}
override fun getItemCount(): Int {
return capabilityDataList.size
}
}
Can anyone tell me what it is I'm doing wrong?
I have a recyclerView inside a fragment, this fragment is located in a viewpager2.
I read data from server and convert them to my ModelClass, them create an ArrayList and pas this array list to recyclerView.
here is my recyclerView item layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#drawable/main_icon_background"
android:elevation="5dp"
android:layoutDirection="rtl"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="#+id/image"
android:layout_width="120dp"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#color/black" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.6"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="#style/vazir_font"
android:textColor="#color/black" />
<TextView
android:id="#+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:ellipsize="end"
android:maxLines="3"
android:textAppearance="#style/vazir_font"
android:textColor="#color/gray"
android:textSize="13sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="1.5dp"
android:layout_marginStart="20dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="8dp"
android:alpha="0.5"
android:background="#color/gray" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/source"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_weight="0.8"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="#style/vazir_font"
android:textColor="#color/colorPrimaryDark"
android:textSize="13sp" />
<TextView
android:id="#+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="end"
android:textAppearance="#style/vazir_font"
android:textColor="#color/colorAccent"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
and here is my recyclerView adapter:
class NewsRecyclerViewAdapter : RecyclerView.Adapter<NewsRecyclerViewAdapter.ViewHolder>() {
private val items = ArrayList<NewsAdapterModel>()
lateinit var onClick: NewsTutorialClickListener
fun add(list: ArrayList<NewsAdapterModel>) {
items.clear()
items.addAll(list)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.news_recycler_model, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.binding(items[position])
}
override fun getItemId(position: Int): Long {
return items[position].id.hashCode().toLong()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun binding(model: NewsAdapterModel) {
itemView.setOnClickListener {
onClick.onClick(model.id)
}
itemView.title.text = model.title
itemView.description.text = model.description
itemView.date.text = Arrange().persianConverter(model.date)
itemView.source.text = model.source
Glide.with(itemView.context)
.load(model.image)
.placeholder(R.mipmap.logo)
.into(itemView.image)
}
}
}
I used this recycler view in my fragment just like this:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
adapter.onClick = this
binding.recyclerView.layoutManager = LinearLayoutManager(context)
binding.recyclerView.adapter = adapter
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.setItemViewCacheSize(20)
scope.launch {
withContext(Dispatchers.Default) {
fetchNews()
}
}
}
my problem is : when I scroll recycler view for first time it is not smooth and it seems some frames are jumped, but when I reach end of recyclerView and scroll to up then scroll down it is very smooth and find. how can I solve this issue?
I also should mention that Items of recyclerView are not much, it's around 10 items.
remove
recyclerView.setHasFixedSize(true)
actually it can cause no harm and indeed improves performance, because recyclerView won't bother calculating size of viewHolders evrytime inside onBindViewHolder. But you have all your TextViews with wrap_content height which will most certainly cause some broken UI in future.
remove
recyclerView.setItemViewCacheSize(20)
This function forces RecyclerView to prematurely inflate and bind 20 extra ViewHolders, so that it can use them straight away without calling OncreateViewHolder and OnBindViewHolder. You should'nt play around with this and just go with the defaults. Although 20 extra viewholders shouldn't have been a big deal, i guess maybe RecyclerView got confused because you forced it create extra 20 on top of existing 10, while you list only contains 10 items. It was taking time to create those extra viewHolders blocking ui thread, but after they were created it seemed smooth.
You don't need to add these lines
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.setItemViewCacheSize(20)
Please remove these lines
There's similar questions, but I haven't been able to find any as of yet that get into the basics.
I'm making a to-do list application and I want it so that when I click the plus button, the user is asked for text and the item is displayed on the screen. Once the text is recieved from the user, I'm not sure how you put this into a textview. add that textview into a constraintlayout(activity_main.xml) and align it with another textview (menutitletext) so that it doesn't default to 0,0. I haven't been able to add code since I genuinely don't know where to start - the answers either are adding imageviews, are in Kotlin or are using a relativelayout. I'd appreciate any help on this.
Someone asked for my design - I think this is what you wanted ?
<androidx.constraintlayout.widget.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="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/divider"
android:layout_width="393dp"
android:layout_height="2dp"
android:layout_marginTop="28dp"
android:background="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/menuaddbutton" />
<ImageButton
android:id="#+id/menusettingsbutton"
android:layout_width="65dp"
android:layout_height="55dp"
android:layout_marginStart="11dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="20dp"
android:src="#drawable/test1w"
app:layout_constraintBottom_toTopOf="#+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="#+id/menutrashbutton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.235" />
<ImageButton
android:id="#+id/menutrashbutton"
android:layout_width="60dp"
android:layout_height="65dp"
android:layout_marginStart="12dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toTopOf="#+id/divider"
app:layout_constraintStart_toEndOf="#+id/menuaddbutton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="#drawable/test1d" />
<ImageButton
android:id="#+id/menuaddbutton"
android:layout_width="78dp"
android:layout_height="59dp"
android:layout_marginStart="22dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="156dp"
android:layout_marginBottom="20dp"
android:background="#android:color/background_light"
app:layout_constraintBottom_toTopOf="#+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="#+id/menueditbutton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.6"
app:srcCompat="#drawable/test1z" />
<ImageButton
android:id="#+id/menueditbutton"
android:layout_width="59dp"
android:layout_height="59dp"
android:layout_marginStart="80dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="20dp"
android:background="#android:color/background_light"
android:onClick="edittitle"
app:layout_constraintBottom_toTopOf="#+id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="#drawable/test1f" />
<TextView
android:id="#+id/menutitletext"
android:layout_width="366dp"
android:layout_height="36dp"
android:layout_marginStart="12dp"
android:layout_marginTop="15dp"
android:gravity="center_horizontal"
android:text="#string/menutitleempty"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/divider" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can use same concept as in here https://stackoverflow.com/a/40527407/3904645
It adds views to ConstraintLayout programmatically and aligns them with programmatic constraints. This is what you need I believe.
For your case
set.connect(childView.getId(), ConstraintSet.TOP, parentLayout.getId(),
ConstraintSet.TOP, 60);
change parentLayout.getId() with the last list item that you have added. You can track what is the last view with some instance variable.
class MainActivity : AppCompatActivity() {
lateinit var parentLayout: ConstraintLayout
var lastView: View? = null
var numberIfViews = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
parentLayout = findViewById<View>(R.id.constraint_layout) as ConstraintLayout
lastView = parentLayout
}
fun addTextView(text: String) {
val set = ConstraintSet()
val childView = TextView(this)
childView.id = View.generateViewId()
childView.text = text
parentLayout.addView(childView, numberIfViews)
set.clone(parentLayout)
set.connect(
childView.id,
ConstraintSet.TOP,
lastView!!.id,
ConstraintSet.TOP,
60
)
set.applyTo(parentLayout)
numberIfViews++
lastView = childView
}
}
Have not tested it but it should work
ConstraintLayout parentLayout = (ConstraintLayout)findViewById(R.id.mainConstraint);
TextView tt= new TextView (this);
// set view id, else getId() returns -1
childView.setId(View.generateViewId());
layout.addView(tt, 0);
you can try this way
I'm currently using DataBinding with a RecyclerView, and I'm getting pretty severe lag when a list first loads. However, after I scroll past a page and it stops creating new viewholders, everything is fine.
During creation, the only thing I'm doing is inflating a layout using DataBindingUtils, so I'm wondering if there are parts that can be improved.
Attached below are relevant code snippets. I'm currently using a library, FastAdapter, so the signatures will not match exactly
When creating a viewholder, I do the following:
override fun createView(ctx: Context, parent: ViewGroup?): View {
val start = System.nanoTime()
val binding: ViewDataBinding = DataBindingUtil.inflate(
LayoutInflater.from(ctx),
layoutRes, parent,
false,
null
)
L.d { "Create view ${(System.nanoTime() - start) / 1000000}" }
return binding.root
}
It appears that for my more complex layout, viewholders take around 30-60 ms each, resulting in notable lag.
Binding and unbinding are like so:
final override fun bindView(holder: ViewHolder, payloads: MutableList<Any>) {
super.bindView(holder, payloads)
val binding = DataBindingUtil.getBinding<Binding>(holder.itemView) ?: return
binding.bindView(holder, payloads)
binding.executePendingBindings()
}
open fun Binding.bindView(holder: ViewHolder, payloads: MutableList<Any>) {
setVariable(BR.model, data)
}
final override fun unbindView(holder: ViewHolder) {
super.unbindView(holder)
val binding = DataBindingUtil.getBinding<Binding>(holder.itemView) ?: return
binding.unbindView(holder)
binding.unbind()
}
open fun Binding.unbindView(holder: ViewHolder) {}
final override fun getViewHolder(v: View): ViewHolder = ViewHolder(v, layoutRes)
Basically, I usually have a single data model that I set, and I implement unbinding myself per viewholder. There doesn't seem to be a problem there.
Reading other articles, it appears that most developers have the same viewholder creation method as I do. Is DataUtilBinding not meant to be done upon creation, or is there something I'm missing?
As an addition, here is my relevant layout xml:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="model"
type="github.fragment.ShortRepoRowItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:windowBackground"
android:foreground="?selectableItemBackground"
android:paddingStart="#dimen/kau_activity_horizontal_margin"
android:paddingTop="#dimen/kau_padding_small"
android:paddingEnd="#dimen/kau_activity_horizontal_margin"
android:paddingBottom="#dimen/kau_padding_small"
tools:context=".activity.MainActivity">
<TextView
android:id="#+id/repo_name"
style="#style/TextAppearance.AppCompat.Medium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#{model.name}"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#tools:sample/full_names" />
<TextView
android:id="#+id/repo_desc"
style="#style/TextAppearance.AppCompat.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:text="#{model.description}"
android:textColor="?android:textColorSecondary"
app:goneFlag="#{model.description}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/repo_name"
tools:text="#tools:sample/lorem/random" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_stars"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:chipIcon="#drawable/ic_star_border"
app:compactNumberText="#{model.stargazers.totalCount}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:layout_constraintWidth_percent="0.12"
tools:text="123" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_forks"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:chipIcon="#drawable/ic_fork"
app:compactNumberText="#{model.forkCount}"
app:layout_constraintStart_toEndOf="#id/repo_stars"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:layout_constraintWidth_percent="0.12"
tools:text="123" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_issues"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:chipIcon="#drawable/ic_issues"
app:compactNumberText="#{model.issues.totalCount}"
app:layout_constraintStart_toEndOf="#id/repo_forks"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:layout_constraintWidth_percent="0.12"
tools:text="1.5k" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_prs"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:chipIcon="#drawable/ic_pull_requests"
app:compactNumberText="#{model.pullRequests.totalCount}"
app:layout_constraintStart_toEndOf="#id/repo_issues"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:layout_constraintWidth_percent="0.12"
tools:text="123" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_language"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#{model.primaryLanguage.name}"
app:chipIcon="#drawable/ic_language"
app:languageColor="#{model.primaryLanguage.color}"
app:layout_constraintEnd_toStartOf="#id/repo_date"
app:layout_constraintStart_toEndOf="#id/repo_prs"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:layout_constraintWidth_percent="0.25"
tools:text="JavaScript" />
<com.google.android.material.chip.Chip
android:id="#+id/repo_date"
style="#style/RepoChips"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:chipIcon="#drawable/ic_time"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/repo_language"
app:layout_constraintTop_toBottomOf="#id/repo_desc"
app:relativeDateText="#{model.pushedAt}"
app:textStartPadding="4dp"
tools:text="#tools:sample/date/mmddyy" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I've tried doing the same thing with two linear layouts instead of ConstraintLayout, but it doesn't seem to make much of a difference.
Code snapshot: https://github.com/AllanWang/GitDroid/tree/f802c991580d70470b422186fc43f46b9cfe2465
I did some more measurements and found out that the culprit was actually the layout inflation (ie 60ms) instead of just the binding (ie 3ms).
To verify, inflate the view as is without DataBindingUtil, then call bind on the view.
From there, the problem was also the use of MaterialChips.
After switching 6 material chips to textviews, inflation time went down 3 folds to around 10-30ms.