View Binding Both Activity and Bottom Bar - android

I am setting up View Binding in my project, and I ran into an issue. I tried to look for documentation regarding this under without luck.
I have my activity, and I have set up View Binding for this:
class AeropressActivity : AppCompatActivity() {
private lateinit var binding: ActivityAeropressBinding
private lateinit var bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAeropressBinding.inflate(layoutInflater)
setContentView(binding.root)
setupBottomSheet()
onClickListeners()
}
private fun setupBottomSheet() {
bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.layout_bottom_sheet))
// OnClickListener for bottomSheetBehavior
bottomSheetBehavior.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
}
)
}
After setting this up, I want to use View Binding for my BottomSheet, as it has some buttons and a Chronometer that I want to add onClickListeners to.
I have added the
lateinit var bindingBottomSheet: LayoutBottomSheetBinding
but inflating this does nothing:
bindingBottomSheet = LayoutBottomSheetBinding.inflate(layoutInflater)
What am I missing to get this to work? Is this even possible?
Edit:
The BottomSheet layout looks like this:
<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/layout_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="140dp"
app:behavior_peekHeight="50dp"
tools:context=".BottomSheetActivity"
app:behavior_hideable="false"
android:background="#drawable/background_bottom_bar"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<ImageView
android:id="#+id/image_view_button_home"
android:src="#drawable/ic_home"
android:layout_width="40dp"
android:layout_height="35dp"
android:layout_marginStart="24dp"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/image_view_button_timer"
android:layout_width="40dp"
android:layout_height="35dp"
android:layout_marginTop="10dp"
android:src="#drawable/ic_stopwatch"
app:layout_constraintLeft_toRightOf="#id/image_view_button_home"
app:layout_constraintRight_toLeftOf="#id/image_view_button_info"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:src="#drawable/ic_info"
android:id="#+id/image_view_button_info"
android:layout_width="40dp"
android:layout_height="35dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toRightOf="#id/image_view_button_timer"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button_bottom_reset"
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Reset"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/image_view_button_info" />
<Chronometer
android:gravity="center_horizontal"
android:textSize="48sp"
android:textColor="#color/white"
android:format="%s"
android:id="#+id/chronometer_bottom_bar"
android:layout_width="0dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:layout_height="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/button_bottom_start"
app:layout_constraintStart_toEndOf="#id/button_bottom_reset"
app:layout_constraintTop_toBottomOf="#id/image_view_button_timer" />
<Button
android:id="#+id/button_bottom_start"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Start"
app:layout_constraintBottom_toTopOf="#id/button_bottom_stop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/image_view_button_home" />
<Button
android:id="#+id/button_bottom_stop"
android:layout_width="100dp"
android:layout_height="40dp"
android:text="Stop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/button_bottom_start" />
</androidx.constraintlayout.widget.ConstraintLayout>

The BottomSheet has its own layout which is added to the activity's layout by using the .
I want to use View Binding for my BottomSheet, as it has some buttons and a Chronometer that I want to add onClickListeners to.
So, you can't access the underlying views of the BottomSheet from the AeropressActivity
First make sure that the BottomSheet layout is wrapped in <layout></layout> tag.
Then make sure to have an id to the <include> so that it allows you access the BottomSheet layout using data binding.
AeropressActivity layout:
<layout>
.
.
<include
android:id="#+id/bottom_sheet"
layout="#layout/bottom_sheet" />
</layout>
Then to access buttons in the BottomSheet layout:
bindingBottomSheet.bottomSheet.myButtonId
Assuming that the button id is: my_button_id

Related

My custom view is not being shown how should I fixed it?

Hello I try to add view binding in my custom view but unfortunately my custom view is not visible, every thing works fine but when I open the activity the custom view is not displayed
my custom view class
class StickerView #JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet?=null,
defStyle:Int = 0
) : ConstraintLayout(context,attributeSet,defStyle),View.OnClickListener {
private val binding = ItemStickerBinding.inflate(LayoutInflater.from(context),this,true)
private var segmentedVerticalSeekBar: SegmentedVerticalSeekBar? = null
private var btn1: ImageButton?=null
private var btn2: ImageButton?=null
private var btn3: ImageButton?=null
private var btn4: ImageButton?=null
private var btn5: ImageButton?=null
private var btn6: ImageButton?=null
private var btn0: ImageButton?=null
init {
init(context)
}
override fun onClick(v: View?) {
when(v?.id){
R.id.appCompatButton ->{
}
R.id.appCompatButton1 ->{
}
R.id.appCompatButton2 ->{
}
R.id.appCompatButton3 ->{
}
R.id.appCompatButton4 ->{
}
R.id.appCompatButton5 ->{
}
R.id.appCompatButton6 ->{
}
}
}
private fun init(context: Context){
inflate(context,R.layout.item_sticker,this)
segmentedVerticalSeekBar = binding.svsLevelView
btn0 = binding.appCompatButton
btn1 = binding.appCompatButton1
btn2 = binding.appCompatButton2
btn3 = binding.appCompatButton3
btn4 = binding.appCompatButton4
btn5 = binding.appCompatButton5
btn6 = binding.appCompatButton6
btn6?.setOnClickListener(this)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(Int.MAX_VALUE,400)
}
}
main activity
<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"
android:background="#color/background"
tools:context=".MainActivity">
<com.example.emoticker.StickerView
android:id="#+id/stickerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<include layout="#layout/item_sticker"/>
</com.example.emoticker.StickerView>
</androidx.constraintlayout.widget.Constraint
my customview layout
item_sticker.xml
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="150dp"
xmlns:tools="http://schemas.android.com/tools"
android:background="#color/background"
tools:context=".StickerView"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.ss.svs.SegmentedVerticalSeekBar
android:id="#+id/svsLevelView"
android:layout_width="60dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:layout_marginStart="20dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
app:backgroundColor="#color/white"
app:cornerRadius="10dp"
app:currentValue="2"
app:delimiterColor="#color/white"
app:isAllRadius="true"
app:layout_constraintBottom_toTopOf="#+id/appCompatButton6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:maxValue="4"
app:progressColor="#color/color_progress"
app:pyramidViewEnable="true"
app:step="1"
app:touchDisabled="false" />
<ImageView
android:layout_width="100dp"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toTopOf="#+id/linearLayout"
app:layout_constraintEnd_toEndOf="#+id/linearLayout"
app:layout_constraintStart_toStartOf="#+id/linearLayout"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="15dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/svsLevelView">
<ImageButton
android:id="#+id/appCompatButton"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/laughing_emoji_svgrepo_com" />
<ImageButton
android:id="#+id/appCompatButton1"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/scare_svgrepo_com" />
<ImageButton
android:id="#+id/appCompatButton2"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/crying_emoji_svgrepo_com" />
<ImageButton
android:id="#+id/appCompatButton3"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/sick_svgrepo_com" />
<ImageButton
android:id="#+id/appCompatButton4"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/shocked_emoji_svgrepo_com" />
<ImageButton
android:id="#+id/appCompatButton5"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/angry_svgrepo_com" />
</LinearLayout>
<ImageButton
android:id="#+id/appCompatButton6"
android:layout_width="50dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_weight="1"
android:background="#color/white"
android:src="#drawable/ic_baseline_subdirectory_arrow_left_24"
app:layout_constraintBottom_toBottomOf="#+id/linearLayout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/linearLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
I appreciate you guys if u help me
sorry for my bad english
You're overriding onLayout but there's no code in it. So when the view needs to lay itself out... it doesn't! Remove the onLayout function and it should work.
Also, don't call inflate in that init function. You already inflated the layout and added it to your custom view here:
private val binding = ItemStickerBinding.inflate(LayoutInflater.from(context),this,true)
If you inflate again, you create a new layout and replace the old one. binding was created from the old layout, and all its references point to the old views.
You also don't want this, it's a duplicate layout:
<include layout="#layout/item_sticker"/>
Just as an example of how I'd write this class with view binding - maybe it helps! It should be easier to work with anyway:
// subclass FrameLayout instead - you're inflating a layout and putting it -inside-
// your custom view, so a FrameLayout (which is meant to hold a single view) is better
// and safer than a ConstraintLayout (which you're not providing constraints for -
// it could break at some point, or act weird)
// I've removed the View.OnClickListener interface because I'm setting the listeners
// another way
class StickerView #JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet?=null,
defStyle:Int = 0
) : FrameLayout(context,attributeSet,defStyle) {
// this inflates your layout and adds it to the view - job done!
private val binding =
ItemStickerBinding.inflate(LayoutInflater.from(context),this,true)
// If you need to refer to a view you've bound, you usually do it through
// the binding object - so "binding.appCompatButton0" etc, and there are ways
// to avoid saying "binding" all the time (see the init block below).
// But if you really want aliases, you could do it this way:
val btn0: ImageButton get() = binding.appCompatButton
init {
// this lets you avoid saying binding.this, binding.that - it can look neater
with(binding) {
// set a single click listener on multiple views
listOf(
appCompatButton, appCompatButton1, appCompatButton2,
appCompatButton3, appCompatButton4, appCompatButton5, appCompatButton6
).forEach { button ->
// call our onClick function with the clicked view
button.setOnClickListener { view -> onClick(view)}
// or setOnClickListener(::onClick)
}
// or you could set each button's action here, like this
appCompatButton1.setOnClickListener { doThing() }
appCompatButton2.setOnClickListener { doSomethingElse() }
}
}
// handle all your button clicks in a function if you want -
// v doesn't need to be nullable
private fun onClick(v: View) {
// because you're using view binding, you have references to all the views -
// so you don't need to use IDs at all
when (v) {
binding.appCompatButton -> { doThing() }
}
}
Personally, if you want the buttons to be named btn0 etc, I'd rename their IDs from appCompatButton to btn0 - that way you can refer to them as binding.btn0, if that's how you prefer to think about it! Give them the names you want to use.
Hope that helps!

How to change background color from fragment using xml file?

I would like to change color of one element in recyclerview on action(for example swipe, but it works fine).
I have used:
viewHolder!!.itemView.setBackgroundColor(Color.GRAY)
It works like on screen, because I have different layout like layout_content.
Gray coolor in wrong place
When I add line like below color doesn't change at all. Please notice, that I had to implement another binding with other layout which uses another .xml file to get some preferences.
layoutBinding.vContent.frameLayout.setBackgroundColor(Color.GREY)
<?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:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/bg_list_item_incoming_leads"
android:paddingBottom="#dimen/spacing_2"
app:cardCornerRadius="#dimen/spacing_8">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="#+id/btnClose"
style="?android:borderlessButtonStyle"
android:layout_width="#dimen/spacing_32"
android:layout_height="#dimen/spacing_32"
android:layout_gravity="end"
android:layout_marginTop="20dp"
android:layout_marginEnd="#dimen/spacing_16"
android:contentDescription="#string/notification_list_close_button_content_description"
android:src="#drawable/ic_icon_grey_primary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="TouchTargetSizeCheck" />
<TextView
android:id="#+id/tvLeadFullName"
style="#style/Regular.13"
android:layout_width="wrap_content"
android:layout_height="27dp"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="2dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/tvSimpleText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvInformation"
app:layout_constraintVertical_chainStyle="spread_inside"
tools:text="%leadfullname" />
<TextView
android:id="#+id/tvProductNotification"
style="#style/Regular.13"
android:layout_width="wrap_content"
android:layout_height="27dp"
android:layout_marginStart="6dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="2dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/tvSimpleText"
app:layout_constraintTop_toBottomOf="#+id/tvInformation"
app:layout_constraintVertical_chainStyle="spread_inside"
tools:text="%product" />
<TextView
android:id="#+id/tvSimpleText"
style="#style/Regular.13"
android:layout_width="wrap_content"
android:layout_height="27dp"
android:layout_marginStart="6dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="6dp"
android:layout_marginBottom="2dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/tvProductNotification"
app:layout_constraintStart_toEndOf="#+id/tvLeadFullName"
app:layout_constraintTop_toBottomOf="#+id/tvInformation"
app:layout_constraintVertical_chainStyle="spread_inside"
tools:text="-" />
<TextView
android:id="#+id/tvInformation"
style="#style/Regular.15"
android:layout_width="211dp"
android:layout_height="27dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside"
tools:text="You received a new Lead" />
<TextView
android:id="#+id/tvData"
style="#style/Regular.13"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="10dp"
android:textColor="#color/black"
app:layout_constraintEnd_toStartOf="#+id/btnClose"
app:layout_constraintTop_toTopOf="parent"
tools:text="11 Oct 2021" />
<TextView
android:id="#+id/tvTime"
style="#style/Regular.13"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/btnClose"
tools:text="hh:ss" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="#color/white">
</solid>
<corners
android:topRightRadius="#dimen/spacing_8"
android:topLeftRadius="#dimen/spacing_8"
android:bottomLeftRadius="#dimen/spacing_8"
android:bottomRightRadius="#dimen/spacing_8">
</corners>
</shape>
How to have access to .xml file and change background from fragment directly to bg_list_item_incoming_leads.xml maybe that would be a solution?
EDIT, additional information:
BaseFragment
abstract class BaseFragmentBindings<VB : ViewBinding, LB : ViewBinding, VM : BaseViewModel>(private val inflate: Inflate<VB>, private val secondInflate: Inflate<LB>) :
Fragment() {
private var binding: VB? = null
protected val layout: VB
get() = binding!!
private var bindingLayout: LB? = null
protected val layoutBinding: LB
get() = bindingLayout!!
abstract val vm: VM
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = inflate.invoke(inflater, container, false)
bindingLayout = secondInflate.invoke(inflater, container, false)
return binding!!.root
}
fragment
#AndroidEntryPoint
class NotificationListFragment :
BaseFragmentBindings<FragmentNotificationListBinding, LayoutListItemNotificationBinding, NotificationListViewModel>
(FragmentNotificationListBinding::inflate, LayoutListItemNotificationBinding::inflate) {
override val vm: NotificationListViewModel by viewModels()
...
}
try below snippet, get reference to background, which is Drawable, check if its ColorDrawable (in your case it is set only in XML then it will always be) and if is then use class casting and set new color, other params should stay unchanged (like rounded corners set with <corners tag, btw. app:cardCornerRadius="#dimen/spacing_8" in main XML set for FrameLayout is no-op, it was left there probably when this item was a CardView)
Drawable background = layoutBinding.vContent.frameLayout.getBackground();
if (background instanceof ColorDrawable)
((ColorDrawable) background).setColor(Color.GRAY);

How to Intialize the Binding Properties in the Fragment to Make Two Way Data Binding Work

So I'm doing a project and I'm completely lost. I have seen how to do data binding with TextViews but I am being asked to do it with EditText Views with Two Way Data Binding. I got up to here so far with it.
The XML File.
<?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="myShoe"
type="com.udacity.shoestore.product.Shoe" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary">
<TextView
android:id="#+id/title_detail_view"
style="#style/title_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:text="#string/add_shoe_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/shoe_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="#string/shoe_name_string"
android:inputType="text"
android:textSize="30sp"
android:text="#={myShoe.name}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_detail_view" />
<EditText
android:id="#+id/shoe_size"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/size_string"
android:inputType="number|numberDecimal"
android:textSize="15sp"
android:text="#={myShoe.size}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_name" />
<EditText
android:id="#+id/company_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/company_string"
android:inputType="text"
android:textSize="15sp"
android:text="#={myShoe.company}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_size" />
<EditText
android:id="#+id/shoe_description"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/description_string"
android:inputType="text"
android:textSize="15sp"
android:text="#={myShoe.description}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/company_name" />
<Button
android:id="#+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="#color/colorPrimaryDark"
android:text="#string/cancel_string"
android:textColor="#android:color/white"
app:layout_constraintBaseline_toBaselineOf="#+id/savee_button"
app:layout_constraintEnd_toStartOf="#+id/savee_button"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/savee_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="88dp"
android:backgroundTint="#color/colorPrimaryDark"
android:text="#string/save_string"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I was told to implement it to a fragment and it should work. But I'm not sure how. Here's the fragment
class ShoeDetailsFragment : Fragment() {
private val viewModel: ActivityViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentShoeDetailsBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_shoe_details,
container, false
)
//initializing the button and clearing the views once canceled
binding.cancelButton.setOnClickListener { v: View ->
v.findNavController().navigateUp()
binding.shoeName.text.clear()
binding.shoeSize.text.clear()
binding.companyName.text.clear()
binding.shoeDescription.text.clear()
}
//initializing the button and saving the info to transfer to the shoeList
binding.saveeButton.setOnClickListener { v: View ->
v.findNavController().navigateUp()
val name = shoe_name.text.toString()
val size = shoe_size.text.toString()
val brand = company_name.text.toString()
val details = shoe_description.text.toString()
viewModel.addShoe(name, size, brand, details)
}
return binding.root
}
}
I am open to any ideas to initialize binding properties so i can use it in both the layout and the fragment. Or am I looking at this the wrong way?
P.S. The XML File is being represented in this fragment
I also did this project for my NanoDegree.
Inside my ViewModel I created 3 variables for each EditText:
MutableLiveData - to update the value inside the viewModel
LiveData to expose value outside viewModel e.g. in a fragment (you won't really need this)
Public Variable to monitor value of MutableLiveData and expose this to your xml thus achieving the 2-Way Binding.
Then I and would create a Shared ViewModel to share data between ShoeDetailsFragment and ShoeListingFragment .
Inside the SharedViewModel
I created 3 variables for each EditText (this is just the first 2 Edittexts):
class MySharedViewModel : ViewModel() {
private val _name = MutableLiveData<String>()
val name: LiveData<String>
get() = _name
var edShoeName = ""
private val _size = MutableLiveData<Double>()
val size: LiveData<Double>
get() = _size
var edSize = ""
......}
For the xml I did exactly what you have done but used the 3rd variable for the 2-Way Data Binding:
<EditText
android:id="#+id/shoe_name"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ems="10"
android:hint="#string/shoe_name_string"
android:inputType="text"
android:textSize="30sp"
android:text="#={mySharedViewModel.edShoeName}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_detail_view" />
<EditText
android:id="#+id/shoe_size"
style="#style/login_style"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/size_string"
android:inputType="number|numberDecimal"
android:textSize="15sp"
android:text="#={mySharedViewModel.edCompany}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/shoe_name" />
I have seen you have included this line of code on your ShoeDetailFragment code:
binding.saveeButton.setOnClickListener { v: View -> ....}
In my case I did it inside SharedViewModel instead:
fun onSaveButtonClick() {
//check value entered for size and set it to 0 if blank
if (edSize == "") {
edSize = "0"
}
//update MutableLiveData with values read live from the EditText
_name.value = edShoeName
_size.value = edSize.toDouble()
//save shoeObject to the _shoeList MutableLiveData
_shoeList.value?.add(Shoe(edShoeName, edSize.toDouble(), edCompany, edDescription))
}
Using DataBinding I moved the onClick bit to xml:
<Button
android:id="#+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="75dp"
android:layout_marginBottom="75dp"
android:background="#drawable/custom_button_background"
android:onClick="#{()->sharedViewModel.onSaveButtonClick()}"
You can also refer to my project.

java.lang.ClassCastException: java.lang.Integer cannot be cast to android.widget.ImageButton

So I'm fairly new to Kotlin.
I am trying to create an onClickListener on an image button to open the share interface, so that the particular video from the recyclerView can be shared via SMS, etc.
I followed various tutorials on how to do this as I am trying to execute this within a fragment, and the app just keeps crashing when I try to open the fragment in question.
I get the following error
java.lang.ClassCastException: java.lang.Integer cannot be cast to android.widget.ImageButton
Here is what my fragment code looks like:
SearchFragment.kt
class SearchFragment : Fragment(), View.OnClickListener
{
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<ClipAdapter.ViewHolder>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View
{
val rootView = inflater.inflate(R.layout.fragment_search, container, false)
loadData()
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
val shareBtn = view.findViewById<ImageButton>(R.id.button_to_share)
shareBtn.setOnClickListener(this)
}
private fun loadData()
{
val service = TwitchServiceBuilder.buildService(TwitchService::class.java)
val requestCall = service.getClips("anerdfails")
requestCall.enqueue(object : Callback<List<Clip>>
{
override fun onResponse(
call: Call<List<Clip>>,
response: Response<List<Clip>>
)
{
if (response.isSuccessful)
{
//process data
recyclerView.layoutManager = GridLayoutManager(activity, 2)
recyclerView.adapter = ClipAdapter(response.body()!!)
} else
{
//output alert
AlertDialog.Builder(activity!!)
.setTitle("API error")
.setMessage("Response, but something went wrong ${response.message()}")
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setIcon(android.R.drawable.ic_dialog_alert)
.show()
}
}
override fun onFailure(call: Call<List<Clip>>, t: Throwable)
{
//process failure
AlertDialog.Builder(activity!!)
.setTitle("API error")
.setMessage("No response, and something went wrong $t")
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setIcon(android.R.drawable.ic_dialog_alert)
.show()
}
})
}
override fun onClick(v: View?)
{
Toast.makeText(activity, "Its toast!", Toast.LENGTH_SHORT).show()
}
}
And here are my 2 layouts for the RecyclerView:
fragment_search.xml
<?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:paddingStart="15dp"
android:paddingTop="?attr/actionBarSize"
android:paddingEnd="15dp"
tools:context=".ui.search.SearchFragment">
<androidx.appcompat.widget.SearchView
android:background="#drawable/search_bar"
android:id="#+id/clipSearch"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="5dp"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_constraintBottom_toTopOf="#+id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="75dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/clipSearch" />
</androidx.constraintlayout.widget.ConstraintLayout>
clip_layout.xml
<?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="wrap_content"
tools:context=".ui.search.SearchFragment">
<VideoView
android:id="#+id/videoClip"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
app:layout_constraintDimensionRatio="w,2:3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/txtTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/videoClip"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtChannel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtTitle"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtGame"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtChannel"
tools:ignore="HardcodedText" />
<TextView
android:id="#+id/txtViews"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtGame"
tools:ignore="HardcodedText" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp"
android:orientation="horizontal"
android:weightSum="2"
android:layout_marginTop="15dp"
app:layout_constraintTop_toBottomOf="#+id/txtViews">
<ImageButton
android:id="#+id/favouriteButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#null"
android:scaleType="fitCenter"
android:src="#drawable/ic_baseline_favorite_border_24" />
<ImageButton
android:id="#+id/button_to_share"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#null"
android:scaleType="fitCenter"
android:src="#drawable/ic_baseline_share_24" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Seems like a simple mistake on my part, but I'm pulling my hair out trying to work out what I've done wrong to cause the error on loading.
Any help would be appreciated.
I see you're trying to find the button "ShareBtn" inside the fragment which is totally wrong.
the "ShareBtn" doesn't belong to the fragment, it belongs to the viewHolder which you have created inside "ClipAdapter"
What you need to do is creating an interface inside "ClipAdapter" and create an object from it inside the Adapter
then call the method which is should the clickListener delegates the click to it
lastly, you should implement it inside the fragment and put whatever logic you want
This link will help you implement it
You are casting ID of view which is an Integer to ImageButton which is a View in this line
val shareBtn = R.id.button_to_share as ImageButton
You should use this instead
val shareBtn = findViewById<ImageButton>(R.id.button_to_share)
UPDATE
Also you should find views after fragment view got created. It means you should call `findViewById` inside `onViewCreated` and not inside `onCreateView`. If you try to find views before view of fragment gets created then you get `NullPointerException` since there is no view yet.

Data Binding inflation is very slow

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.

Categories

Resources