is there a faster/simpler way to make this:? I'm creating a numpad in my Android App which I use 10 buttons for and a delete button. Right now I'm doing it like this but like this it's gonna take a lot of code.
num0.setOnClickListener(){
editTextNumberPassword.append("0")
}
num1.setOnClickListener(){
editTextNumberPassword.append("1")
}
num2.setOnClickListener(){
editTextNumberPassword.append("3")
}
Add all views into a list and then use the index:
listOf(num0, num1, num2)
.withIndex()
.forEach { (index, view) ->
view.setOnClickListener { editTextNumberPassword.append("$index") }
}
Related
I know that it is possible to perform a click on a view like this :
view.PerformClick()
How do I do it on TextInputLayout EndIcon button?
Update
The problem is that I have a bunch of InputLayouts and use a generic function to set the click listeners on them like so
fun setTextInputLayoutListeners(
inputLayout: TextInputLayout, editText: TextInputEditText,
actionSet: () -> Unit,
actionClear: () -> Unit
) {
with (inputLayout) {
setOnClickListener { actionSet() }
setEndIconOnClickListener { actionClear() }
}
editText.setOnClickListener { actionSet() }
}
and call it with different parameteres like this
setTextInputLayoutListeners(
categoryInputLayout, categoryEditText, { onCategoryClick() }, { onCategoryClear() }
)
setTextInputLayoutListeners(
dateInputLayout, dateEditText, { onDateClick() }, { onDateClear(calendar) }
)
so I'm looking for a generic solution, sort of
inputLayout.EndIcon.PerformClick()
textinput.setEndIconOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do some code
}
});
hope it helps..
EndIcon in TextInputLayout is of type CheckableImageButton and its id is R.id.text_input_end_icon (use R.id.text_input_start_icon for StartIcon). To simulate clicks, you need to find the button using findViewById, then cast it as a CheckableImageButton and call performClick().
In your case:
inputLayout.findViewById<CheckableImageButton>(R.id.text_input_end_icon)?.performClick()
Thank you so much for you clear and helpful answer.I did a little trick that worked and i wanna share my answer to everyone who will need it.
First thing i did is get the string value of my view :
Log.d("tag", String.valueOf(v));
i got that : com.google.android.material.internal.CheckableImageButton{ba604a VFED..C.. ...P.... 8,3-104,99 #7f090125 app:id/text_input_end_icon}
and like i suspected then icon is a different view of the text field layout with different id (in the end of the string value of the view).
So i changed my if condition from if (v.getId() == R.id.start_date_Layout) to if (v.getId() == R.id.text_input_end_icon), and it work now
i hope this answer will be helpful to someone, thank you again for all your answer
I'm working on a quiz app for my project at university, and I'm trying to save button and boolean that states if the answer is correct or not in data class and save all buttons in a temporary list so I can add onclick listener for all of them later. But when I'm trying to access boolean value, it just turns into number 904. Here's my code regarding these buttons.
val ansBtnList: MutableList<ButtonDataClass> = mutableListOf()
--------------------------------------------------------------
val ans = ButtonDataClass(Button(this), quizToShow.getValue(planets[0]).answers[i].isRight)
--------------------------------------------------------------
ansBtnList.add(ans)
--------------------------------------------------------------
for (i in 0..3) {
ansBtnList[i].btn.setOnClickListener { Log.d(null, ansBtnList[i].btn.right.toString()) }
}
Thanks in advance!
EDIT: ButtonDataClass code:
data class ButtonDataClass (var btn: Button, var right: Boolean)
It's not the right that you think it is. btn is the button. The button has a right field that is related to its coordinate on screen. You should try ansBtnList[i].right.toString() instead.
btn.right refers to right variable of Button class, not to the one present in the ButtonDataClass
replace: ansBtnList[i].btn.setOnClickListener { Log.d(null, ansBtnList[i].btn.right.toString()) }
with: ansBtnList[i].btn.setOnClickListener { Log.d("your_tag_id", ansBtnList[i].right) }
Of course you are not accessing the right field. ansBtnList[i].btn.right gives you the right position of your button in pixels.
Rather do ansBtnList[i].right
I want to do actions with 9 buttons.Is there anyway to do action with 9 buttons in one time,not one by one.
If I could add variable to findViewById() I would solve problem.
For example I want to select 9 button at once(with for loop) like below
for(i in 1..9){
findViewById(R.id.button+i).text="New Text"
}
This way not working,please suggest optimal ways.
I want like this in javascript equivalent:
document.getElementById("button"+i)
Declare this in your activity:
var buttons: ArrayList<Button> = arrayListOf()
and in onCreate():
for (i in 1..100) {
buttons.add(findViewById<Button>(resources.getIdentifier("button" + i, "id", this.getPackageName())))
}
Now you have all your buttons in a list.
if you want to change the state of your buttons, say disable them all:
buttons.forEach { it.isEnabled = false }
You can set tag for each button (like button1, button2, etc.) and do something like this
for(i in 1..9){
(view.findViewWithTag("button"+i.toString()) as Button).text = "New Text"
}
I am trying to test an EditText that already contains some text using Espresso. The problem is that when I use typeText(), the cursor is placed at an arbitrary position within the text. I tried performing click() before using typeTextIntoFocusedView but the cursor is sometimes placed at the beginning of the EditText. I want to know is it possible to set the cursor at the end of the EditText before typing text into it?
A better way would be to use Espresso the way it's meant to be used: with actions on view matchers.
Example in Kotlin:
class SetEditTextSelectionAction(private val selection: Int) : ViewAction {
override fun getConstraints(): Matcher<View> {
return allOf(isDisplayed(), isAssignableFrom(EditText::class.java))
}
override fun getDescription(): String {
return "set selection to $selection"
}
override fun perform(uiController: UiController, view: View) {
(view as EditText).setSelection(selection)
}
}
Example usage:
onView(withId(R.id.my_text_view).perform(SetEditTextSelectionAction(selection))
An extra advantage over manually doing findViewById() is that you can combine this with matchers like withSubString("my text") if you don't have the ID of the view.
By the way: to change this into setting selection at the end of text you can simply remove the selection: Int constructor argument and change setSelection(selection) to setSelection(view.text.lastIndex).
The only way I have found to do this is to get a reference to the EditText itself and use EditText#setSelection(). For example, to move the cursor to the end of the current text:
val activity = activityRule.activity
val tv = activity.findViewById<EditText>(R.id.edittext)
activity.runOnUiThread { tv.setSelection(tv.text.length) }
I've had success by inserting the KeyCodes for "Home" and "End". These work just like on your desktop keyboard, by moving the cursor to either the beginning or end of the EditText. For example:
onView(withId(R.id.myView))
.perform(pressKey(KeyEvent.KEYCODE_MOVE_HOME))
To move to the end, you can use KeyEvent.KEYCODE_MOVE_END, and you can move left or right using KeyEvent.KEYCODE_DPAD_LEFT and KeyEvent.KEYCODE_DPAD_RIGHT.
I wanted to post my answer since I just had this problem and none of the other answers solved my problem.
I used a GeneralClickAction to click on the right side of the edit text which put the cursor at the end of the EditText where I wanted it. After that I used the TypeTextAction and disabled the tapToFocus behavior by passing in false to the constructor:
onView(withId(R.id.edit_text))
.perform(
new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0, null),
new TypeTextAction(text, false)
);
Basically I'd like to attach a single OnClickListener to multiple views inside a ConstraintLayout.
Before migrating to the ConstraintLayout the views where inside one layout onto which I could add a listener. Now they are on the same layer with other views right under the ConstraintLayout.
I tried adding the views to a android.support.constraint.Group and added a OnClickListener to it programmatically.
group.setOnClickListener {
Log.d("OnClick", "groupClickListener triggered")
}
However this does not seem to work as of the ConstraintLayout version 1.1.0-beta2
Have I done something wrong, is there a way to achieve this behaviour or do I need to attach the listener to each of the single views?
The Group in ConstraintLayout is just a loose association of views AFAIK. It is not a ViewGroup, so you will not be able to use a single click listener like you did when the views were in a ViewGroup.
As an alternative, you can get a list of ids that are members of your Group in your code and explicitly set the click listener. (I have not found official documentation on this feature, but I believe that it is just lagging the code release.) See documentation on getReferencedIds here.
Java:
Group group = findViewById(R.id.group);
int refIds[] = group.getReferencedIds();
for (int id : refIds) {
findViewById(id).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// your code here.
}
});
}
In Kotlin you can build an extension function for that.
Kotlin:
fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
Then call the function on the group:
group.setAllOnClickListener(View.OnClickListener {
// code to perform on click event
})
Update
The referenced ids are not immediately available in 2.0.0-beta2 although they are in 2.0.0-beta1 and before. "Post" the code above to grab the reference ids after layout. Something like this will work.
class MainActivity : AppCompatActivity() {
fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Referenced ids are not available here but become available post-layout.
layout.post {
group.setAllOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
val text = (v as Button).text
Toast.makeText(this#MainActivity, text, Toast.LENGTH_SHORT).show()
}
})
}
}
}
This should work for releases prior to 2.0.0-beta2, so you can just do this and not have to do any version checks.
The better way to listen to click events from multiple views is to add a transparent view as a container on top of all required views. This view has to be at the end (i.e on top) of all the views you need to perform a click on.
Sample container view :
<View
android:id="#+id/view_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#+id/view_bottom"
app:layout_constraintEnd_toEndOf="#+id/end_view_guideline"
app:layout_constraintStart_toStartOf="#+id/start_view_guideline"
app:layout_constraintTop_toTopOf="parent"/>
Above sample contains all four constraint boundaries within that, we can add views that to listen together and as it is a view, we can do whatever we want, such as ripple effect.
To complement the accepted answer for Kotlin users create an extension function and accept a lambda to feel more like the API group.addOnClickListener { }.
Create the extension function:
fun Group.addOnClickListener(listener: (view: View) -> Unit) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
usage:
group.addOnClickListener { v ->
Log.d("GroupExt", v)
}
The extension method is great but you can make it even better by changing it to
fun Group.setAllOnClickListener(listener: (View) -> Unit) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
So the calling would be like this
group.setAllOnClickListener {
// code to perform on click event
}
Now the need for explicitly defining View.OnClickListener is now gone.
You can also define your own interface for GroupOnClickLitener like this
interface GroupOnClickListener {
fun onClick(group: Group)
}
and then define an extension method like this
fun Group.setAllOnClickListener(listener: GroupOnClickListener) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener { listener.onClick(this)}
}
}
and use it like this
groupOne.setAllOnClickListener(this)
groupTwo.setAllOnClickListener(this)
groupThree.setAllOnClickListener(this)
override fun onClick(group: Group) {
when(group.id){
R.id.group1 -> //code for group1
R.id.group2 -> //code for group2
R.id.group3 -> //code for group3
else -> throw IllegalArgumentException("wrong group id")
}
}
The second approach has a better performance if the number of views is large since you only use one object as a listener for all the views!
While I like the general approach in Vitthalk's answer I think it has one major drawback and two minor ones.
It does not account for dynamic position changes of the single views
It may register clicks for views that are not part of the group
It is not a generic solution to this rather common problem
While I'm not sure about a solution to the second point, there clearly are quite easy ones to the first and third.
1. Accounting position changes of element in the group
This is actually rather simple. One can use the toolset of the constraint layout to adjust the edges of the transparent view.
We simply use Barriers to receive the leftmost, rightmost etc. positions of any View in the group.
Then we can adjust the transparent view to the barriers instead of concrete views.
3. Generic solution
Using Kotlin we can extend the Group-Class to include a method that adds a ClickListener onto a View as described above.
This method simply adds the Barriers to the layout paying attention to every child of the group, the transparent view that is aligned to the barriers and registers the ClickListener to the latter one.
This way we simply need to call the method on the Group and do not need to add the views to the layout manually everytime we need this behaviour.
in Constraintlayout 2.0.0,you can use Layer to resolve multiple views click event,and also support scale animation
For the Java people out there like me:
public class MyConstraintLayoutGroup extends Group {
public MyConstraintLayoutGroup(Context context) {
super(context);
}
public MyConstraintLayoutGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyConstraintLayoutGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOnClickListener(OnClickListener listener) {
for (int id : getReferencedIds()) {
getRootView().findViewById(id).setOnClickListener(listener);
}
}
}
This is not propagating click states to all other children however.
fun ConstraintLayout.setAllOnClickListener(listener: (View) -> Unit) {
children.forEach { view ->
rootView.findViewById<View>(view.id).setOnClickListener { listener.invoke(this) }
}
}
And then
.setAllOnClickListener {
do something
}