I mean the method from the class View that allows to put a tag to an object of that class like this:
view.setTag(1)
Which would be the equivalent in Swift?
You can set tag for view this way.
view.tag = 1
I may be late, but the actual answer given by #Muhammad Afzal is not exactly equivalent to .setTag() in Android.
Swift .tag only allows the use of Int values, while Android .setTag() allows the use of any Object value.
To get an equivalent functionality, one should create a simple custom class like the following:
public class UITagView:UIView{
var Tag:Any?
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setTag(_ tag:Any){
self.Tag = tag
}
func getTag()->Any{
return self.Tag!
}
}
If view is an instance of the class, then view.setTag() would be the the same in Android and Swift.
However, if the tags that you need are always Integers, it is easier to use view.tag, as #Muhammad Afzal said.
Note: As in Android, when recovering an object with the .getTag() method, it should be parsed to the class it belongs to (view.getTag() as! SomeClass).
Related
I'm trying to create a fake class for my repository to test a view model.
As far as I understood, the key element here is to create two classes with a common interface so both classes would contain the same methods.
The problem is I get a Type mismatch when trying to initialize an object.
I tried to do the same in a simplified manner:
class fakeClass1 : fakeInterface {
override fun getAllData(): String {
return ""
}}}
class fakeClass2 : fakeInterface {
override fun getAllData(): String {
return ""
}}
interface fakeInterface {
fun getAllData(): String}
val fakeClass: fakeClass1 = fakeClass2()
But that didn't work either.
What am I missing?
Ok, I figured it out.
I was wrong to think that those two classes should be interchangeable.
I solved it by making the ViewModel take the common interface in its constructor instead of the actual repository class. This allows the ViewModel to take any class which implement this interface as it's repository.
I think you worked it out, but just so you're clear (this is an important, fundamental thing!)
val fakeClass: fakeClass1 = fakeClass2()
This is defining a variable called fakeClass that refers to an object with the fakeClass1 type. Then you assign an object with the fakeClass2 type.
But a fakeClass2 is not a fakeClass1, neither is a superclass of the other, so you can't treat one as the other. Your example is simple, but imagine you added coolFunction() to fakeClass1 - they'd now happen to have different structures, and trying to call that method on an object that doesn't have it would cause a crash.
The only thing those classes have in common, is that they both have the fakeInterface type - they are fakeInterfaces, and that guarantees they implement the stuff in that interface (your getAllData function in this case). So if you treat them both as that type instead:
val fakeClass: fakeInterface = fakeClass2()
you can use either one, because they're both fakeInterfaces (similar to how Ints and Doubles are different but they're both Numbers). Because fakeClass is now a fakeInterface, you can only access the functions and properties that a fakeInterface has - you can't call coolFunction() even if you happened to pass in a fakeClass1, because fakeInterface doesn't have that.
(You could cast the variable to fakeClass1, basically saying "oh by the way this object is actually this type as well", but at that point the type system can't guarantee you're correct unless you're explicitly checking fakeClass is fakeClass1, and it'll warn you if that's the case)
The Java tutorials are pretty good and they'll give you an overview about how the types each form a kind of "contract" you work with
I use findViewById to get references to views in a custom DialogClass outside the view hierarchy. Since I want these references throughout the dialog class, and since they are not available until the call to setContentView, I use lazy field initialization in the dialog class definition:
private val balanceView : TextView? by lazy { dialog?.findViewById(R.id.balanceTextView)}
Later, in a separate function that runs after the call to setContentView, I have two commands related to balanceView:
private fun setupBalance(){
balanceView!!.visibility = View.VISIBLE
balanceView.text = presentation.balance
}
I expected the second command to compile since we are ensuring balanceView is not null in the first command. However, the compiler underlines the second command with this message:
Smart cast to 'TextView' is impossible, because 'balanceView' is a property that has open or custom getter
I was wondering how to interpret this message - to my knowledge the lazy keyword doesn't make a variable open.
The use of a property delegate like lazy means it has a custom getter. A custom getter prevents the compiler from being able to ensure that the value returned by the property will be the same in your two consecutive calls, so it cannot smart-cast.
Since balanceView is nullable, you shouldn't be using !! either, or your app will crash when balanceView is null. The correct way to write your function (if you don't know if balanceView is currently null) would be like this:
private fun setupBalance(){
balanceView?.apply {
visibility = View.VISIBLE
text = presentation.balance
}
}
If there's no chance of it being null, don't use a nullable property, and then you won't have to worry about null-safe calls and smart-casting. But you seem to also have a nullable dialog that it is dependent on. I don't know what's going on higher-up-stream, so I can't help with that.
This question is specific for extension function of Kotlin used in Android development.
So Kotlin provides us capability to add certain extension behavior in to a class to extend the based class behavior.
Example: (take from my current Android project, for viewAssertion in testing with Espresso)
fun Int.viewInteraction(): ViewInteraction {
return onView(CoreMatchers.allOf(ViewMatchers.withId(this), ViewMatchers.isDisplayed()))
}
In my usecase, I can use it like:
R.id.password_text.viewInteraction().perform(typeText(PASSWORD_PLAIN_TEXT), pressDone())
All good, except this extension function gives the extended behavior to all Int objects, not just the View IDs in Android, which is not good at all.
The question is if there is any way to give context for this Int, like in Android we have #IdRes in Android support annotation for such given case above?
You can't differentiate between an Int from the resources and a normal Int. It is the same class and you are adding an extension to all the classes of the Int type.
An alternative could be to create your own wrapper of Int:
class IntResource(val resource: Int) {
fun viewInteraction(): ViewInteraction {
return onView(CoreMatchers.allOf(ViewMatchers.withId(resource), ViewMatchers.isDisplayed()))
}
}
And then us it like this:
IntResource(R.id.password_text).viewInteraction().perform(typeText(PASSWORD_PLAIN_TEXT), pressDone())
This concerns two features in the Data Binding guide (https://developer.android.com/topic/libraries/data-binding/index.html) : BindingAdapters and BindingConverions. The following examples are from the guide:
#BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
if (oldPadding != newPadding) {
view.setPadding(newPadding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
}
#BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
My questions are:
If I have two different BindingAdapters with the same method signature, how can I indicate which one to use for a given view? (without using custom field name variants such as myPaddingLeftOne, myPaddingLeftTwo, etc)
Similarly, if I have two BindingConversions which have the same input and return types, how can I indicate which to use in a given situation?
The short answer is, you can't. If you have two Binding adapters with the same target types and same value types, the last one compiled will be the one that is used. This way you can override the implementations in the library.
Use different application namespace attributes to distinguish the two or use a method to make a conversion in your expression if that will help.
The same is true for BindingConversions.
Use a custom attribute and a BindingAdapter or an conversion function in your expression if you need to convert sometimes and not others. Binding conversions are powerful, but you should limit when you use them as they are activated any time the conversion can make an expression work. The one example used in the data binding library is converting an integer color to a ColorDrawable for use when Drawable is the parameter.
I was working on android data-binding and came across the scenario that we can set a model using following two ways:
User user = new User("User", "Abc"); // this is a model
dataBinding.setVariable(BR.user, user);
dataBinding.executePendingBindings(); // and we have to do this... Why?
and we can also set like:
binding.setUser(user);
Can anyone explain this what the difference between these two?
User Model:
public class User{
public String fName;
public String lName;
public User(String fName, String lName){
this.fName = fName;
this.lName = lName;
}
}
Consider the case when you have a abstract class which does not share a common binding layout (except for of course the superclass ViewDataBinding which all binding layouts inherit from):
public abstract classs EditorActivityFragment<T extends ViewDataBinding> {
In this class' onCreateView() you won't be able to use any generated methods to set your variable to the binding, as there's no common superclass besides the ViewDataBinding, thus you will be forced to use reflection, or you can use the convenience method setVariable():
binding.setVariable(BR.viewModel,myViewModel);
I hope that helps explain the use case for this method better.
They do the same thing. According to the docs, sometimes the type of the variable can't be determined, so you will have to use the setVariable() method. Under normal circumstances, the setX() method(s) will be generated. You are better off using the generated methods.
Here is the link from official docs if anyone is still not clear.
Link to doc - Click here!
An excerpt from the doc as shared by #JarettMillard
boolean setVariable (int variableId,
Object value)
Set a value in the Binding class.
Typically, the developer will be able to call the subclass's set method directly. For example, if there is a variable x in the Binding, a setX method will be generated. However, there are times when the specific subclass of ViewDataBinding is unknown, so the generated method cannot be discovered without reflection. The setVariable call allows the values of variables to be set without reflection.
Some more references from their official docs
Link!
override fun onBindViewHolder(holder: BindingHolder, position: Int) {
item: T = items.get(position)
holder.binding.setVariable(BR.item, item);
holder.binding.executePendingBindings();
}
This is useful when you don't have access to a specific binding class but a more generic version, like ViewDataBinding (which is like the base class of all bindings). In such a case, you can use this method to set a property, like in the above case we have used for item variable.