I have a separate layout that I want to call onClick and update a field on callback
<include
android:onClick="#{() -> viewModel.changeItem(2)}"
layout="#layout/item"
app:attr="#{viewModel.title}"
app:desc="#{viewModel.description}"
app:active="#{viewModel.isSelected}"
/>
But it returns the following binding error:
Cannot find the setter for attribute 'android:onClick' with parameter
type lambda on com.X.databinding.ItemBinding.
But I can binding on other views
<TextView
android:onClick="#{() -> viewModel.changeItem(1)}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
What I suggest you is to call the onClick directly from code.
Setup an ID for your view in your .xml, then set something like this :
val item = findViewById(R.id.your_id) etc.
item.onClick { functionYouWantToCall() }
However, I do not know if this is possible to set an ID or an onClickListener on an include layout.
If you can not do it, simply put your include inside a LinearLayout, then set your onClick on it.
I used to set onClicks in the .xml too, but I think it is much more efficient to set it in the code.
Moreover, I do not know if you can set arguments in a function you call from the xml.
Related
I'm trying to build a progress bar in Android Studio where the maximum value is dynamically selected based off data in a table that I have. In a class, I create a global variable and set this to be an integer.
I want to then read this in my layout.xml file, so I can have dynamically changing progress bars. Ie something like:
<ProgressBar
android:id="#+id/determinateBar"
style="#android:style/Widget.ProgressBar.Horizontal"
android:progress="67"
android:max= // IntVariable here />
How can I pull a variable from a class into this file?
You can set the progress bar value programmatically like this
In an activity
ProgressBar progressBarView = findViewById(R.id.progressBar);
// later when you want to set the value
progressBar.setProgress(progressStatus)
or in a fragment
View root = inflater.inflate(R.layout.fragment_example, container, false);
ProgressBar progressBarView = root.findViewById(R.id.progressBar);
// later when you want to set the value
progressBar.setProgress(progressStatus)
You have two different ways to achieve what you want, first is the recommended way that would be to enable data binding in your project by adding this to your app-level build.gradle file inside the android tag:
dataBinding {
enabled = true
}
Then you must add the presenter of the activity (that holds the variable you want) to the activity's layout XML like this:
<data>
<variable
name="presenter"
type="com.main.TheNameOfYourPresenter" />
</data>
Finally, you can use the value stored in your presenter in the progress bar like this:
<ProgressBar
android:id="#+id/determinateBar"
style="#android:style/Widget.ProgressBar.Horizontal"
android:progress="67"
android:max="#{presenter.someIntStoredInThePresenter}"/>
The second way is to change it programmatically by accessing the progress bar inside your activity class and setting the max attribute as you want it:
progressBar.setMax(someInt);
Problem: Glitch bug with RecyclerView's child's item ImageView
I have a RecyclerView.Each item has ImageView with id "like" which is basically an empty star.
When we click on some item's "Like", for example, the first, the star is changing from empty filled in yellow, which means that item is liked.
When we click on the first item everything is OK, everything is as it should be, but at the same time, we have a bug, changing the sixth item's "like" to a filled star, which shouldn't be done, as this item wasn't liked yet.
If we click on second item's like - the seventh item would have the same bug.
To fill the item's in ViewHolder I have a model - Recipe.
open class Recipe constructor(open var label: String,
open var image: String,
open var url: String,
open var ingredients: RealmList<Ingredient>?,
open var calories: Float,
open var totalWeight: Float,
open var id: Int,
open var isFavorite: Boolean) : RealmObject()
So when clicking on some item I check whether it isFavorite or not, and change the "like" ImageView accordingly.
Have anyone got any ideas how I should fix this?
I tried to turn off recycling of items in the adapter, but unfortunately, in this case, the "liked" state isn't saved too.
OnClick listener for "like" button
itemView.like.onClick {
if (recipe.isFavorite) {
itemView.like.image = ContextCompat.getDrawable(itemView.context, R.drawable.ic_star_before_like)
recipe.isFavorite = false
DatabaseService.removeRecipeFromFavorites(recipe)
itemView.context.toast("Recipe removed from favorites.")
} else {
itemView.like.image = ContextCompat.getDrawable(itemView.context, R.drawable.ic_star_after_like)
recipe.isFavorite = true
DatabaseService.addNewFavoriteRecipe(recipe)
itemView.context.toast("Recipe added to favorites.")
}
}
XML-File
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.8"
android:gravity="center|right"
android:orientation="horizontal">
<ImageView
android:id="#+id/like"
android:layout_width="#dimen/icon_size_card"
android:layout_height="#dimen/icon_size_card"
android:padding="5dp"
android:layout_marginRight="#dimen/icon_margin_card"
android:src="#drawable/ic_star_before_like" />
<ImageView
android:id="#+id/share"
android:layout_width="#dimen/icon_size_card"
android:layout_height="#dimen/icon_size_card"
android:padding="5dp"
android:layout_marginRight="#dimen/icon_margin_card"
android:src="#drawable/ic_share" />
<ImageView
android:id="#+id/save"
android:layout_width="#dimen/icon_size_card"
android:layout_height="#dimen/icon_size_card"
android:padding="5dp"
android:layout_marginRight="#dimen/icon_margin_card"
android:src="#drawable/ic_save_recipe" />
<ImageView
android:id="#+id/expand"
android:layout_width="#dimen/icon_size_card"
android:layout_height="#dimen/icon_size_card"
android:padding="5dp"
android:layout_marginRight="#dimen/icon_margin_card"
android:src="#drawable/ic_down_arrow" />
</LinearLayout>
RecyclerView reuses the viewholders it creates with onCreateViewHolder. To put it simply, think of it as the views going off screen on the top coming back around on the bottom, and vica versa. Therefore, the nice way of solving what you want is the following:
Set the icon to the right drawable in the onBindViewHolder method. This way, whether a viewholder is being bound for the first time, or had a drawable reflecting a recipe's favourited state already, it will be refreshed properly.
Without knowing what your adapter looks like, this should give you an idea of how to do this (the example code assumes you have your recipes in some list called recipes):
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val recipe = recipes[position]
val itemView = holder.itemView
itemView.like.image = ContextCompat.getDrawable(itemView.context,
if(recipe.isFavorite) R.drawable.ic_star_before_like else R.drawable.ic_star_after_like)
}
In the listener, modify the Recipe the same way you're doing it now, but instead of then setting the image directly, notify the adapter that the recipe at the given position has changed (notifyItemChanged(position)), which will make it re-bind that item, refreshing the icon to reflect the new state. Again, I don't know where you're setting up the listener, but I assume you know what position the recipe is at in the list there.
RecyclerView is built to cache views and reuse them. When you scroll, your views are recycled and loaded from the memory.
After you have made changes to your underlying data model attached to the adapter, it is important to notify the adapter about the same. In your onClick method, after you've made the changes, make sure you call adapter.notifyDataSetChanged() or just notifyDataSetChanged() if you're calling it directly from your adapter class.
In a relative layout I see a pattern as:
<TextView
android:id=“#+id/txt_id”
etc
android:layout_alignBottom="#+id/some_other_txt”
etc
/>
<TextView
android:id="#+id/some_other_txt"
etc
/>
I thought +id is used only when creating an id for a widget. Is this a kind of “trick” to layout a widget relative to another widget declared later in the file?
UPDATE:
This question is specifically about the RelativeLayout possitioning. Not about the difference in syntax in general as the linked question
Its just a reference point for placing the different elements in the layout builder.
From this SO answer
The first time you reference an ID, use the #+ prefix, which tells the resource builder to add the ID,
Here you're making a forward reference to a widget & id that's not already defined:
<TextView
android:id=“#+id/txt_id”
etc
android:layout_alignBottom="#+id/some_other_txt”
etc
/>
Here you are defining it:
<TextView
android:id="#+id/some_other_txt"
More context here
I want to learn - if there is a way - how to define a custom view type, and use it in XML layouts.
For example, I have a custom button that looks like this:
<LinearLayout>
<ImageView />
<TextView />
</LinearLayout>
I know I can save this as mybutton.xml, and do <include layout="#layout/mybutton" />, but is there a way that I can use this like this:
<MyButton />
?
I would also like to be able to instantiate this new, custom class in Java code. Like this :
MyButton mb = new MyButton();
Should I define MyButton as a Java class? If so, what should I extend, and how ?
Thanks for any help !
You will need to define a custom class for your object.
MyButton.java
public class MyButton extends View{}
You will have to create the constructor to inflate your XML document, and then call the object in your XML by the fully qualified package name.
<com.example.MyButton/>
I have a layout in XML as follows:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:onClick="myButton.performClick" />
myButton is a member of this activity, is it possible use a class member method here?
is it possible use a class member method here?
According to the API - no. The API says:
Name of the method in this View's context to invoke when the view is clicked. This name must correspond to a public method that takes exactly one parameter of type View.
No, as the docs say (emphasis mine):
you must declare a public void sayHello(View v) method of your context
(typically, your Activity)
If you wanted to execute a method on a field, you could wrap it I suppose.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:onClick="performClick" />
Then:
public void performClick(View view) {
myButton.performClick(view);
}
but that almost defeats the purpose of the XML onClick attribute (making things more concise). Is there a good reason for performClick to be on your button instead of your activity anyway?
Lastly, doing this on the LinearLayout is fine. The attribute is supported for all Views.
onClick doesn't go in the Linear Layout tag. It goes in the tag of the button whose on click you want to set.
So you do it like this:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="methodName"/>
</LinearLayout>