How to reverse View Binding from custom tab layout in Android? - android

I made a custom tab item for my tab layout and initialized it using view binding as follows:
val tabView = CustomTabBinding.inflate(LayoutInflater.from(mContext), null, false)
tabView.tvCustomTabTitle.text = it.title
tabView.tvCustomTabCount.visibility = View.GONE
Now when the user selects/unselects the tab I want to change the appearance of this custom view. Usually I achieved this using kotlin synthetics as follows:
fun setOnSelectView(tabLayout: TabLayout, position: Int = 0) {
val tab = tabLayout.getTabAt(position)
val selected = tab?.customView
if (selected != null)
selected.tv_custom_tab_title?.apply {
setTextColor(mContext.getColorCompat(R.color.colorAccent))
typeface = setFont(true)
}
selected?.tv_custom_tab_count?.apply {
setBackgroundResource(R.drawable.bullet_accent)
mContext.getColorCompat(android.R.color.white)
}
}
But now how do I achieve this using view binding?
I am using the method of findViewById():
fun Context.setOnSelectView(tabLayout: TabLayout, position: Int = 0) {
val tab = tabLayout.getTabAt(position)
val selected = tab?.customView
if (selected != null){
val title = selected.findViewById<TextView>(R.id.tv_custom_tab_title)
val count = selected.findViewById<TextView>(R.id.tv_custom_tab_count)
title.apply {
setTextColor(getColorCompat(R.color.colorAccent))
typeface = setFont(true)
}
count.apply {
setBackgroundResource(R.drawable.bullet_accent)
getColorCompat(android.R.color.white)
}
}
}
but I am hoping there is a better way to do this. If yes, then please do help me out.

Late reply but this is how I used view binding for custom tab layout, hope it helps
custom_tab.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tv_custom_tab_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/tv_custom_tab_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
your activity/fragment:
val tab = binding.tabLayout.getTabAt(position)
tab.setCustomView(R.layout.custom_tab)
val tabBinding = tab.customView?.let {
CustomTabBinding.bind(it)
}
tabBinding?.tvCustomTabTitle?.text = "your title here"

Related

Android - Disable Popup being dismissible when Material Exposed Dropdown Menu is open

Currently, I have a Material Exposed Dropdown Menu in a popup window, but when I tap outside the popup window, I don't want it to be dismissed if the Material Exposed Dropdown Menu is open. As for what I have tried so far: I can set a setOnClickListener for the AppCompatAutoCompleteTextView with a boolean flag that changes when it is opened or closed, but this only impact whether the text is clicked in the Material Exposed Dropdown. If I set an onClickListener to the TextInputLayout it only works if I tap the border of the Material Exposed Dropdown Menu. If I setEndIconOnClickListener on arrow, it overrides whatever listener is attached by default and the dropdown menu is no longer opened, and the arrow no longer gets flipped, even if I set the Dropdown menu to open manually. I want a unified way to handle this so that the popup window is not dismissible when the material exposed dropdown menu is open. Not sure how much help it will be, but here is the code I have that is currently handling the menus:
private fun addIngredient(view: View) {
val fragmentWidth = view.width
val units = resources.getStringArray(R.array.add_ingredient_measurement_units)
val volUnits = resources.getStringArray(R.array.volumetric_measurement_units)
val massUnits = resources.getStringArray(R.array.mass_measurement_units)
val layoutInflater: LayoutInflater = context?.applicationContext?.getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
#SuppressLint("InflateParams")
val addInventoryPopup = layoutInflater.inflate(R.layout.add_inventory_popup, null)
val conversionLayoutContainer = addInventoryPopup.findViewById<LinearLayout>(R.id.add_ingredient_conversion_container)
val closeAddInventoryPopupFab = addInventoryPopup.findViewById<FloatingActionButton>(R.id.add_ingredient_close_fab)
val unitsMenu = addInventoryPopup.findViewById<AppCompatAutoCompleteTextView>(R.id.add_ingredient_units_menu)
val arrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_menu_item, units)
unitsMenu.setAdapter(arrayAdapter)
unitsMenu.addTextChangedListener {
conversionLayoutContainer.removeAllViews()
val currentText = it.toString()
if (volUnits.contains(currentText) || massUnits.contains(currentText)) {
val conversionLayout = layoutInflater.inflate(R.layout.convert_units, conversionLayoutContainer as ViewGroup)
val conversionHeaderText = conversionLayout.findViewById<TextView>(R.id.conversion_text)
val firstUnitMenu = conversionLayout.findViewById<AppCompatAutoCompleteTextView>(R.id.first_unit_menu)
firstUnitMenu.text = it
val secondUnitMenu = conversionLayout.findViewById<AppCompatAutoCompleteTextView>(R.id.second_unit_menu)
if (volUnits.contains(currentText)) {
conversionHeaderText.text = getString(R.string.vol_conversion_text)
val firstUnitArrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_menu_item, volUnits)
val secondUnitArrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_menu_item, massUnits)
firstUnitMenu.setAdapter(firstUnitArrayAdapter)
secondUnitMenu.setAdapter(secondUnitArrayAdapter)
} else if (massUnits.contains(currentText)) {
conversionHeaderText.text = getString(R.string.mass_conversion_text)
val firstUnitArrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_menu_item, massUnits)
val secondUnitArrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_menu_item, volUnits)
firstUnitMenu.setAdapter(firstUnitArrayAdapter)
secondUnitMenu.setAdapter(secondUnitArrayAdapter)
}
} else {
Toast.makeText(requireContext(), "No Unit", Toast.LENGTH_LONG).show()
}
}
val popupWindow = PopupWindow(
addInventoryPopup,
(fragmentWidth * 0.90).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT,
true
)
closeAddInventoryPopupFab.setOnClickListener { popupWindow.dismiss() }
popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0)
}
and here is the part XML file of the layout I am using to inflate the Popup window that contains the material exposed dropdown menu:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/add_ingredient_units_menu_container"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="54dp"
android:hint="#string/unit">
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="#+id/add_ingredient_units_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cursorVisible="false"
android:drawablePadding="-12dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:inputType="none"
android:padding="0dp"
android:text="#string/none"
android:textSize="15sp"
android:maxLines="1"/>
</com.google.android.material.textfield.TextInputLayout>
Also my targetSDK is 33 and my min is 26.

switchbox change state on rotation

I develop a simple table-app, and the problem is, that the switch-box will change the state or doesn't reset the state correcly on change rotation.
befor rotating it looks like
this
after rotating it looks like
this
The hint state will set correct on change but the box is not displayed correctly. What has gone wrong? Actually the views should be updated on any changes in the same way...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Switch
android:id="#+id/tv_outputItemBoolean"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:trackTint="#color/switch_track_selector"
android:thumbTint="#color/switch_thumb_selector"
android:clickable="true"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingBottom="2.5dp"
android:paddingTop="2.5dp"
android:textSize="20sp"
android:gravity="center"
/>
</LinearLayout>
fun getView(position: Int, rowView: TableRow?, parent: TableLayout): TableRow {
val rowView = TableRow(parent!!.context)
val rowData = this.getItem(position)
for (i in 0..columnCounts - 1) {
var data: ColumnData<*>? = rowData[i]
rowView.addView(this.getColumnView(data, null, rowView))
}
return rowView
}
private fun getColumnView(
col: ColumnData<*>?,
resycleView: View?,
parent: ViewGroup
): View {
return when (col?.type) {
DataType.TimeStamp -> {
val view = TimeStampPicker.View(parent.context)
view.timeStampPicker.timeStampInMillis = col.data as Long
view
}
DataType.Boolean ->
{
val c= parent.context
val view =
LayoutInflater.from(c)
.inflate(R.layout.output_item_boolean, null)
val switch: Switch = view.findViewById(R.id.tv_outputItemBoolean)
val value = col.data as Boolean
var state = switch.isChecked
Log.v("XXXXXXXXXXXXXXXX", value.toString()+","+state.toString())
switch.isChecked=value
state = switch.isChecked
Log.v("XXXXXXXXXXXXXXXX", value.toString()+","+state.toString())
switch.hint=c.getText(DataType.stringResourceOf(value))
view
}
else -> {
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.ouput_item_simple_text, null)
val tv: TextView = view.findViewById(R.id.tv_outputItemSimpleText)
tv.text = if (col != null) col.data.toString() else "NULL"
view
}
}
}
the output log print shows the right state befor and after setting.
V/XXXXXXXXXXXXXXXX: false,false
V/XXXXXXXXXXXXXXXX: false,false
V/XXXXXXXXXXXXXXXX: true,false
V/XXXXXXXXXXXXXXXX: true,true
V/XXXXXXXXXXXXXXXX: true,false
V/XXXXXXXXXXXXXXXX: true,true
V/XXXXXXXXXXXXXXXX: false,false
V/XXXXXXXXXXXXXXXX: false,false

How to increment counter of notification badge from adapter kotlin

I am trying to build an E-commerce app having basic functionalities. I want my number of items in the cart to reflect on the action bar. Currently, my UI looks like this.
For creating the notifications this is what I have done:
cart_layout.xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="55sp"
android:paddingRight="10sp"
android:gravity="center">
<ImageView
android:id="#+id/cart_img"
android:layout_width="30sp"
android:layout_height="30sp"
android:background="#drawable/shopping"
android:foreground="?attr/selectableItemBackgroundBorderless" />
<TextView
android:id="#+id/item_count"
android:layout_width="18sp"
android:textAlignment="center"
android:layout_height="18sp"
android:textColor="#android:color/white"
android:text="0"
android:textSize="12sp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="21dp"
android:layout_marginTop="5dp"
android:background="#drawable/itemcount" />
</RelativeLayout>
</RelativeLayout>
My cart_menu.xml inside the menu directory looks like:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/select_cart"
app:showAsAction="always"
app:actionLayout="#layout/cart_layout"
android:title="Cart"/>
<item android:id="#+id/toolbar_search"
android:title="Search"
android:icon="#drawable/ic_search_black_24dp"
app:showAsAction ="always"
app:actionViewClass="android.widget.SearchView"/>
</menu>
My onCreateOptionsMenu functions inside Activity looks like this:
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.cart_menu, menu)
val count:View = menu!!.findItem(R.id.select_cart).actionView
val itemText:TextView = count.findViewById(R.id.item_count)
if(cartList.size == 0){
itemText.visibility = View.INVISIBLE
}
if(cartList.size > 0) {
itemText.visibility = View.VISIBLE
itemText.text = cartList.size.toString()
}
return true
}
Here cartList is the MutableList of items in the cart.
What I want to do is update the counter of this cart whenever the user clicks "Add to cart" button.
The click listener for this button is present inside Adapter class.
My onclicklistener inside onBindViewHolder inside ProductAdapter.kt looks like this:
holder.addToCart.setOnClickListener {
holder.count.number = 1.toString()
Log.d("Product","Clicked ${product.name}, count = ${holder.count.number}")
val cartItemObj = CartItem(product.name,product.imageUrl, product.size, product.price, holder.count.number)
val db = CartDatabase(context)
val result = db.insertData(cartItemObj)
if(result == (-1).toLong()){
Log.d("ProductAdapter","Error in Inserting values")
return#setOnClickListener
}
holder.addToCart.visibility = View.INVISIBLE
holder.count.visibility = View.VISIBLE
cartList.add(cartItemObj)
// SOME CODE HERE TO UPDATE THE NUMBER OF ITEMS IN CART
}
I don't understand how should I achieve this since I am unable to access the Textview inside the Adapter.
The link to the complete project is:
Here
Define cart count
var cartCounter: Int? = 0
//private lateinit var itemText: TextView
lateinit var itemText: TextView
Then Update it in onCreateOptionsMenu method
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.cart_menu, menu)
val count:View = menu!!.findItem(R.id.select_cart).actionView
itemText:TextView = count.findViewById(R.id.item_count)
if(cartList.size == 0){
itemText.visibility = View.INVISIBLE
}
if(cartList.size > 0) {
cartCounter = cartList.size;
itemText.visibility = View.VISIBLE
itemText.text = cartCounter.toString()
}
return true
}
Finally when user click on add to cart update it again
holder.addToCart.setOnClickListener {
holder.count.number = 1.toString()
val cartItemObj = CartItem(product.name,product.imageUrl, product.size, product.price, holder.count.number)
val db = CartDatabase(context)
val result = db.insertData(cartItemObj)
if(result == (-1).toLong()){
Log.d("ProductAdapter","Error in Inserting values")
return#setOnClickListener
}
holder.addToCart.visibility = View.INVISIBLE
holder.count.visibility = View.VISIBLE
cartList.add(cartItemObj)
cartCounter = cartCounter + 1;
// or cartCounter = cartCounter + quantity_of_item;
itemText.text = cartCounter.toString()
}
I am unable to access the Textview inside the Adapter
Your adapter should not know anything about the menu and its items. But it can expose some observable field which can be monitored by an entity owning the menu - I guess it is either your Fragment or Activity. The easiest way to achieve this nowadays is by using LiveData. Create a new instance of it somewhere in your adapter:
class YourAdapter ... {
val cartSize = MutableLiveData<Int>()
}
and update it from your click listener. Now you can observe this livedata from your fragment like this
override fun onViewCreated(...) {
adapter.cartSize.observe(viewLifecycleOwner, Observer {
cartSize -> updateMenuCounter(cartSize)
}
}
The last step here is to implement the updateMenuCounter which can access the previously created MenuItem and update its TextView.

constraintlayout.widget.Group animation not working with TransitionManager

does anyone has any idea why animating constraintlayout.widget.Group visibility with TransitionManager is not working? Isn't this widget made for these kind of things?
It is working if hiding or showing items after separating views from Group
<androidx.constraintlayout.widget.Group
android:id="#+id/cardHeadersGroup"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:constraint_referenced_ids="cardSystemHeader,cardSimpleHeader,cardCombinedHeader"
app:layout_constraintBottom_toBottomOf="#+id/cardCombinedHeader"
app:layout_constraintEnd_toEndOf="#+id/cardSystemHeader"
app:layout_constraintStart_toStartOf="#+id/cardSimpleHeader"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible"/>
val headersGroup = binding.cardHeadersGroup
val slideIn = Slide()
slideIn.slideEdge = Gravity.BOTTOM
slideIn.mode = Slide.MODE_IN
slideIn.addTarget(headersGroup)
TransitionManager.beginDelayedTransition(binding.root as ViewGroup, slideIn)
headersGroup.visibility = VISIBLE
I've been recently working with TransitionManager and ConstraintLayout.Group and found it to be very buggy.
Eventually I decided to dump the whole ConstraintLayout.Group and created an in-code AnimationGroup (similar to the in-xml ConstraintLayout.Group):
class AnimationGroup(vararg val views: View) {
var visibility: Int = View.INVISIBLE
set(value) {
views.forEach { it.visibility = value }
field = value
}
}
and an extension function for the Transition:
private fun Transition.addTarget(animationGroup: AnimationGroup) {
animationGroup.views.forEach { viewInGroup -> this.addTarget(viewInGroup) }
}
That way you can do the following (almost exactly the same code, but simpler xml - no ConstraintLayout.Group):
val headersGroup = AnimationGroup(
binding.cardSystemHeader,
binding.cardSimpleHeader,
binding.cardCombinedHeader
)
val slideIn = Slide()
slideIn.slideEdge = Gravity.BOTTOM
slideIn.mode = Slide.MODE_IN
slideIn.addTarget(headersGroup)
TransitionManager.beginDelayedTransition(binding.root as ViewGroup, slideIn)
headersGroup.visibility = VISIBLE
We can also extract the Group's referenced views with simple extension function:
fun Group.getReferencedViews() = referencedIds.map { rootView.findViewById<View>(it) }

How to use anko spinner?

I'm trying to add a spinner inside an alert using anko. My code so far looks like this:
alert(getString(R.string.alert)) {
positiveButton("Cool") { toast("Yess!!!") }
customView {
linearLayout {
textView("I'm a text")
padding = dip(16)
orientation = LinearLayout.VERTICAL
spinner(R.style.Widget_AppCompat_Spinner) {
id = R.id.spinner_todo_category
prompt = "Select a Category"
}
}
}
}.show()
but I get compilation errors because apparently that's not how to call a spinner. I've been looking at the docs (Anko GitHub Wiki) but it says nothing about spinners.
Thanks in advance
One solution :
class AddActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val items = listOf(Friend("bla","bla",50),Friend("bla","bla",50));
val adapterFriends = ArrayAdapter(this,R.layout.mon_spinner,items)
verticalLayout {
val friends = spinner { adapter = adapterFriends }
val wine = editText()
button("Say Hello") {
onClick { toast("Hello, ${wine.text}!") }
}
}
}
}
with this layout (mon_spinner.xml) :
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="14sp"
android:textColor="#color/colorPrimary"
android:spinnerMode="dialog"
android:text="XXX"
/>
It's all right !!
Try this in your AnkoComponent:
spinner {
adapter = ArrayAdapter.createFromResource(
ctx,
R.array.your_string_array,
android.R.layout.simple_spinner_dropdown_item)
}

Categories

Resources