I have a very simple code in XML:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:text="#={viewModel.password}"
android:enabled="#{viewModel.inputEnabled}">
Now when viewModel.inputEnabled is a MutableLiveData<Boolean> underneath, it simply doesn't work, edit is always enabled, regardless of value of inputEnabled. However, it takes only to change inputEnabled to an ObservableField<Boolean> (and switch setValue to set) such that it immediately starts working.
Why is that so? How can I make MutableLiveData work correctly?
Note, that this field is used in three places (to enable/disable form during processing).
I ran in to same issue....make sure to call following in your activity/fragment:
binding.setLifecycleOwner(this)
Related
This is my first android project,
Matter of fact first programming project ever.
I have an ImageButton that I want to hide when it's disabled,
The enabled/disabled state is done using xml connection to a viewmodel,
so I can't reference the fragment to do it programmatically.
I tried using a selectorlist to pass in a grayed out version of the image, but I want to hide the button completely not just the src image.
the button in the fragment layout:
<ImageButton
android:id="#+id/work_button"
android:src="#drawable/ic_desktop_windows_black"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:enabled="#{timerViewModel.pcButtonState}"
android:onClick="#{() -> timerViewModel.activityTypeWork()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/gaming_button"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.40"
tools:ignore="ContentDescription" />
The variable controlling the disabled/enabled state:
val pcButtonState = Transformations.map(lastSession){
it == null
}
This is done so the button is enabled only when last session is null
So is there anyway to control android:visibility or android:alpha through a selector, or something?
Thank you...
Edit
Problem solved, both my answer and Henry Twist's are correct,
Personally I prefer Henry's approach because it's way easier to implement and maintain.
I don't think there is a need to make the solution this complex, if a button should be hidden when disabled and visible when enabled, you don't need to use the enabled/disabled state at all, you can just set the visibility dependant on timerViewModel.pcButtonState.
So for example:
android:enabled="true"
android:visibility="#{timerViewModel.pcButtonState ? View.VISIBLE : View.GONE}"
Edit
In order you use external classes in data binding you have to import them in your data tag, so:
<data>
<import type="android.view.View" />
</data>
Just so, if anyone else came across a similar weird situation
I found a workaround,
By setting up an observer object in the fragment to observe the variable controlling the state
val pcButton = binding.pcButton
timerViewModel.pcButtonState.observe(viewLifecycleOwner, Observer { state ->
state.let {
if (it == true) {
binding.pcButton.visibility = View.VISIBLE
}
}
})
Now I can programmatically adjust the button properties according to the state of the variable
very newbie in Android Studio Kotlin and am struggling to pass a value from an edittext object to a float variable, but it is proving to be a challenge. I was able to do it but only after making use of 2 additional variables; however I can't believe it can't be done directly.
This is how I managed to do it:
var temp: EditText = findViewById(R.id.txtPriceA)
var temp2: String = temp.getText().toString()
var priceA: Float = temp2.toFloat()
The XML for the edittext is this:
<EditText
android:id="#+id/txtPriceA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:autofillHints=""
android:gravity="center"
android:hint="#string/enter_price_a"
android:inputType="numberDecimal"
android:textColor="#0E0B0B"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="#+id/textView5"
app:layout_constraintHorizontal_bias="0.526"
app:layout_constraintStart_toStartOf="#+id/textView5"
app:layout_constraintTop_toBottomOf="#+id/textView5"
tools:text="$23.45" />
I thank you all in advance for any help.
Ray.
You can skip the intermediate variable like so:
val priceA = findViewById(R.id.txtPriceA).getText().toString().toFloat()
If you are just getting started, and only getting the hang of the framework you should consider this as the only way.
After you got the hang of things I think you should take a look at viewmodels and databinding, that way you will be able to write an inverse binding adapter that would map the value of the text field to a variable in your viewmodel as a float directly.
As you get more comfortable with kotlin, you could also write an extension property for this purpose. Combined with kotlin synthetic view binding your code could look like this:
val priceA = txtPriceA.floatValue
Good luck with your android endeavors!
if i have this button:
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/button" />
which is more advisable in kotlin when it comes to calling views?
this:
val buttonVar: Button = findById(R.id.buuton)
buttonVar.setOnClickListener{
//my code
}
or:
button.setOnClickListener{
//my code
}
When it comes to performance in Kotlin , this is more advisable
button.setOnClickListener{
//my code
}
Because calling views by their ID directly will generate a local view cache.
So the when the view is called the first time kotlin plugin will execute findViewById just a single time, and the next time the view gets called, it will get recovered from the cache . So accesing that view will be faster.
You can refer to this link for more informations enter link description here
I hope this will help you, don't forget to accept the answer if it helps you.
First one is recommended now. The reason is because if you do it the second way, you would be using kotlinx synthetic which is no longer a recommended practice. Source
Okay, so I have a ViewModel with a getter getTitle() that returns MutableLiveData<String>.
<EditText
style="#style/Widget.EditText.FullWidth"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/label_title"
android:inputType="text"
android:text="#={ viewModel.title }" />
This works fine at first: the EditText contains the value of the MutableLiveData when it first appears. However, if the value of this data is updated using MutableLiveData.setValue() (such as by another EditText, or from my code), then the value inside of the text box does not change. How do I fix this?
This works properly in the new version of Android Studio, which supports binding to LiveData objects properly.
So I recently started messing around with Espresso in one of my existing Android projects.
Everything went pretty decently, until I came to find AutoCompleteTextView in my program. I don't seem to understand how to properly click the first thing in the autocomplete list. I'm actually not even sure which to use, onView() or onData() in this instance.
By some reasons which I don't know, AStupidNoob's solution doesn't work. So I found another one:
onView(withText("Spinner Item"))
.inRoot(RootMatchers.isPlatformPopup())
.perform(click());
The AutoCompleteTextView itself
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/textInputLayout2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<AutoCompleteTextView
android:id="#+id/product"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="#string/product"
android:singleLine="true"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
I think I found a bit of a cleaner method than the accepted answer!
onData(equalTo("ITEM")).inRoot(RootMatchers.isPlatformPopup()).perform(click());
The breakdown:
onData(x) This will find the view rendering the data object matching x in the drop down. The data is provided by the Adaptor given to the AutoCompleteTextView, so it can be an object of any type that Adaptor provides, it probably won't be a View. You'll want to use the standard hamcrest core matchers for this (equalTo, instanceOf, etc...) rather than (withText, withId, etc...). It might be a pain to try and find what object this is and how to match it, but there isn't a neater way: with a lot of items in your adapter some of the views won't even be in the hierarchy yet, so onView can't work! onData will make sure to load the views that match your data. Checkout here (this what onData returns) and here (this loads the matching data)
inRoot(RootMatchers.isPlatformPopup()) So it turns out the dropdown menu is on another window than the default window your activity runs in. So we have to specify that we want to search that window. The accepted answer uses RootMatchers.withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView()))) which seems to match any window that is not the default one.
Anyways HTH someone else.
For anyone still running into this issue, despite trying out the accepted solution above, I managed to get it working with help from a github issue I uncovered. For reference, I am using Robolectric 4.6, which I believe may be the reason why I am requiring a different solution from non-instrumented tests.
The solution I came up with (to verify an item is appearing in an AutoCompleteTextView popup is:
fun testAutoCompleteTextViewEntry() {
onView(withId(R.id.editText_search))
.perform(typeTextIntoFocusedView("x"), showDropDown())
onView(withText("xyz"))
.inRoot(RootMatchers.isPlatformPopup())
.check(matches(isDisplayed()))
}
// Somewhere else in your code
fun showDropDown(): ViewAction =
object : ViewAction {
override fun getDescription(): String = "Shows the dropdown menu of an AutoCompleteTextView"
override fun getConstraints(): Matcher<View> = allOf(
isEnabled(), isAssignableFrom(AutoCompleteTextView::class.java)
)
override fun perform(uiController: UiController, view: View) {
val autoCompleteTextView = view as AutoCompleteTextView
autoCompleteTextView.showDropDown()
uiController.loopMainThreadUntilIdle()
}
}
So i finally figured it out, thanks to this previous question:
Testing autocomplete textview using espresso tool
Ill just post my version of it for people who might use it in future.
onData(instanceOf("Whatever your arrayadapter contains".class)).inRoot(RootMatchers.withDecorView(not(is(mActivityRule.getActivity().getWindow().getDecorView())))).perform(ViewActions.click());
Following on from the answer from AStupidNoob on 28 July 2017...
To click on a specific row number of the drop-down list, you can use this:
onData(anything())
.atPosition(2)
.inRoot(RootMatchers.isPlatformPopup())
.perform(click());
To click on a specific item in a specific row number of the drop-down list, you can use this:
onData(anything())
.atPosition(2)
.inRoot(RootMatchers.isPlatformPopup())
.onChildView(withId(R.id.button_on_layout))
.perform(click());
Since the place predictions are inside a recyclerview, after typing the name of the placement, if you want to click on the first option in the predicted list, you can go with one of the RecyclerViewActions methods (actionOnItemAtPosition).
The key is to find out the id of the autocompleted places. It is set up by Google Place SDK but not yourself so might not be that straightforward to find out.
This works for my project, see if it can help:
onView(ViewMatchers.withId(com.google.android.libraries.places.R.id.places_autocomplete_list)).perform(RecyclerViewActions.actionOnItemAtPosition(0,click()));