I made a very simple test application with one activity and one layout. The onClick doesn't trigger the first time it is pressed, as it should.
The activity:
package com.example.mytest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText ed1 = (EditText) findViewById(R.id.editText1);
ed1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "1", Toast.LENGTH_LONG)
.show();
}
});
}
}
The layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:ems="10" >
<requestFocus />
</EditText>
<EditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/editText1"
android:ems="10" />
</RelativeLayout>
If you run this application, and click on the second editText and then back on the first one, it will not trigger the onClick. You can keep selecting back and forth and it will not trigger the onClick at all. I need this basic functionality, but haven't been able to think of a way to get it to work. Ideas?
Notice
I have tried all of the methods recommended on my API level 16 physical device and my API level 8 emulator, but I get the same problem.
Clarification
When editText1 is focused and is clicked on, then the onClick method fires. If editText2 is focussed, and then editText1 is clicked, it doesn't fire. Big problem.
Overview, when a user interacts with any UI component the various listeners are called in a top-down order. If one of the higher priority listeners "consumes the event" then the lower listeners will not be called.
In your case these three listeners are called in order:
OnTouchListener
OnFocusChangeListener
OnClickListener
The first time the user touches an EditText it receives focus so that the user can type. The action is consumed here. Therefor the lower priority OnClickListener is not called. Each successive touch doesn't change the focus so these events trickle down to the OnClickListener.
Buttons (and other such components) don't receive focus from a touch event, that's why the OnClickListener is called every time.
Basically, you have three choices:
Implement an OnTouchListener by itself:
ed1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(MotionEvent.ACTION_UP == event.getAction())
editTextClicked(); // Instead of your Toast
return false;
}
});
This will execute every time the EditText is touched. Notice that the listener returns false, this allows the event to trickle down to the built-in OnFocusChangeListener which changes the focus so the user can type in the EditText.
Implement an OnFocusChangeListener along with the OnClickListener:
ed1.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus)
editTextClicked(); // Instead of your Toast
}
});
This listener catches the first touch event when the focus is changed while your OnClickListener catches every other event.
(This isn't a valid answer here, but it is a good trick to know.) Set the focusable attribute to false in your XML:
android:focusable="false"
Now the OnClickListener will fire every time it is clicked. But this makes the EditText useless since the user can no longer enter any text...
Note:
getApplicationContext() can create memory leaks. A good habit is to avoid using it unless absolutely necessary. You can safely use v.getContext() instead.
I'm probably too late to the party, but here is a code snipped which fixes the issue with onClick() not being called:
ed1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP && !v.hasFocus()) {
// onClick() is not called when the EditText doesn't have focus,
// onFocusChange() is called instead, which might have a different
// meaning. This condition calls onClick() when click was performed
// but wasn't reported. Condition can be extended for v.isClickable()
// or v.isEnabled() if needed. Returning false means that everything
// else behaves as before.
v.performClick();
}
return false;
}
});
make edit text clickable..
In XML android:clickable="true"
or in code
ed1.setClickable(true);
then do
ed1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getBaseContext(), "1",Toast.LENGTH_SHORT).show();
}
});
This happens because the first tap gains the focus into the view. The next tap triggers the click.
If you are inflating the view dynamically, do this:
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false"
If this doesn't work, try applying it on the parent view as well.
Its the most simplest way to work with date picker.
private DatePickerDialog datePickerDialog;
EditText etJoiningDate;
etJoiningDate=(EditText)findViewById(R.id.etJoiningDate);
etJoiningDate.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
final Calendar cldr = Calendar.getInstance();
int day = cldr.get(Calendar.DAY_OF_MONTH);
int month = cldr.get(Calendar.MONTH);
int year = cldr.get(Calendar.YEAR);
// date picker dialog
datePickerDialog = new DatePickerDialog(TestActivity.this,
new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
etJoiningDate.setText(dayOfMonth + "/" + (monthOfYear + 1) + "/" + year);
}
}, year, month, day);
datePickerDialog.show();
break;
}
return false;
}
});
public class TestProject extends Activity implements OnClickListener {
TextView txtmsg;
EditText ed1, ed2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txtmsg = (TextView) findViewById(R.id.txtmsg);
ed1 = (EditText) findViewById(R.id.edt1);
ed2 = (EditText) findViewById(R.id.edt2);
ed1.setOnClickListener(this);
ed2.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(v==ed1){
txtmsg.setText("1");
Toast.makeText(this, "first",Toast.LENGTH_SHORT).show();
}
if(v==ed2){
txtmsg.setText("2");
Toast.makeText(this, "second",Toast.LENGTH_SHORT).show();
}
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<EditText
android:id="#+id/edt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:ems="10" />
<EditText
android:id="#+id/edt2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/edt1"
android:layout_marginTop="14dp"
android:ems="10" />
<TextView
android:id="#+id/txtmsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="#+id/edt2"
android:layout_below="#+id/edt2"
android:layout_marginRight="22dp"
android:layout_marginTop="160dp"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
Took me a minute to figure this out one time when this happened to me. My ImageButton with a setOnClickListener and onClick didn't seem to fire and then I realized it was actually underneath another element in my xml layout, so I turned this:
<RelativeLayout>
<ImageButton>
<LinearLayout></LinearLayout>
</RelativeLayout>
into this:
<RelativeLayout>
<LinearLayout></LinearLayout>
<ImageButton>
</RelativeLayout>
and suddenly the ImageButton was not being overlapped by the other layout since it was now added later to the parent layout and was now on top and works every time. Good luck, always fun when basic stuff suddenly seems to stop working
Avoid using a FocusChangeListener since it will behave erratically when you don't really need it (eg. when you enter an activity). Just set an OnTouchListener along with your OnClickListener like this:
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
view.requestFocus();
break;
}
return false;
}
This will cause your EditText to receive focus at first, and your onClick to function properly the first time.
Simple, Reuseable Kotlin Solution
I started with two custom extension functions:
val MotionEvent.up get() = action == MotionEvent.ACTION_UP
fun MotionEvent.isIn(view: View): Boolean {
val rect = Rect(view.left, view.top, view.right, view.bottom)
return rect.contains((view.left + x).toInt(), (view.top + y).toInt())
}
Then listen to touches on the Edittext. This will only fire if initial ACTION_DOWN event was originally on the Edittext, and still is.
myEdittext.setOnTouchListener { view, motionEvent ->
if (motionEvent.up && motionEvent.isIn(view)) {
// Talk your action here
}
false
}
Related
I have a simple button that looks like:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:tag="general"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#343535"
android:orientation="vertical"
tools:context=".fragments.GeneralFragment">
<Button
android:id="#+id/hello"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center_vertical"
android:onClick="onClick"
android:text="#string/hello" />
Instead of static, these buttons should now be dynamic
Button button = (Button) layout.getChildAt(0);
for(String text : readFromSharedPreferences) {
// Set the layout
Button btn = new Button(this.getContext());
btn.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
btn.setText(text);
btn.setTag(text);
btn.setLayoutParams(button.getLayoutParams());
btn.setBackground(button.getBackground());
layout.addView(btn);
The static button has an animation when I click on it. That looks like this:
But the dynamic button has no animation. So when I click on it, nothing happens. That looks like this:
How can I add this animation to my dynamic buttons?
Update
I have figured out that my loop contains an on-touch listener. That looks like this:
btn.setOnTouchListener(new OnSwipeTouchListener(getContext()) {
// No code in here
});
If I remove that listener (even if it contains no code), the animation works great but I would like to keep it, because of my swipe function that is placed into it.
That is my whole code:
// Swiping to link
btn.setOnTouchListener(new OnSwipeTouchListener(getContext()) {
#Override
public void onSwipeLeft() {
super.onSwipeLeft();
// Alert to ask
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Delete");
builder.setMessage("Do you want to delete?");
// Delete
builder.setPositiveButton("Yes", (dialog, which) -> {
// Set the SharedPreferences as String
ArrayList<String> currentSharedPreferences = readFromSharedPreferences(getContext());
currentSharedPreferences.remove(btn.getTag().toString());
Gson gson = new Gson();
String currentSharedPreferencesAsText = gson.toJson(currentSharedPreferences);
// Update the SharedPreference k-text
SharedPreferences mPrefs = getContext().getSharedPreferences("k-texts", Context.MODE_PRIVATE);
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putString("k-text", currentSharedPreferencesAsText);
prefsEditor.apply();
// Start the animation
btn.animate()
.translationX(-btn.getWidth())
.alpha(0.0f)
.setDuration(300)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
btn.clearAnimation();
btn.setVisibility(View.GONE);
Snackbar.make(view, "Entry deleted.", Snackbar.LENGTH_LONG).setAction("Delete", null).show();
}
});
});
// Cancel
builder.setNegativeButton("No", (dialog, which) -> {
// Silence is golden
});
builder.show();
}
#Override
public void onClick() {
MainActivity mainActivity = new MainActivity();
Tts tts = new Tts(getContext(), _mediaPlayer, mainActivity.barTop, mainActivity.barBottom);
try {
tts.say(btn.getTag().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
});
Well I could use
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
Then the animation will work but onClick() wouldn't work anymore.
Another update
I had a similar problem on another view. There my static button was not having a click effect. Then I have just simply added android:foreground="?attr/selectableItemBackground" and it worked! The same way I have just tried with my dynamic button. So I have added btn.setForeground(button.getForeground()); but that doesn't do anything.
Use MaterialButton instead of Button. MaterialButton is a subtype of Button that supports additional features. The Button that is in your XML layout is actually a MaterialButton if you're using a Material Components theme. The theme automatically swaps out Buttons for MaterialButtons when your XML is inflated. So, when dynamically creating buttons in your Java code, you must use MaterialButton if you want it to match the original.
Also, when using MaterialButton, never call setBackground() because this causes undefined behavior. It likely will prevent the ripple effect from occurring as well.
Alternatively, you can define your Button in its own XML file, even with the layout params it needs for LinearLayout. Then inflate the XML each time you need another button.
for(String text : readFromSharedPreferences) {
Button btn = requireContext().getLayoutInflater()
.inflate(R.layout.my_button, layout, true); // true adds it to layout
btn.setText(text);
btn.setTag(text);
}
I have a button in my Fragment. Which supposed to turn into R.drawable.role_button_pressed then i press it or if my button is already pressed it supposed to turn into original R.drawable.role_button then i press it. But then i am pressing my button it need to be pressed twice to change it state.
My xml looks like this:
<Button
android:id="#+id/role"
android:layout_width="40dp"
android:layout_height="40dp"
android:gravity="center"
android:layout_marginTop="#dimen/margin_role_buttons_top"
android:layout_marginRight="#dimen/margin_role_buttons_right"
android:layout_marginBottom="#dimen/margin_role_button_bot"
android:text="#string/jungle"
android:textColor="#android:color/black"
android:layout_below="#id/divider4"
android:layout_toLeftOf="#id/role_mid_lookingfor"
android:background="#drawable/role_button" />
and on click method
boolean isPressed = true;
Button rolebutton = (Button) v.findViewById(R.id.role);
rolebutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!isPressed){
rolebutton.setBackgroundResource(R.drawable.role_button_pressed);
isPressed=true;
}else if(isPressed==true){
rolebutton.setBackgroundResource(R.drawable.role_button);
isPressed=false;
}
}
});
You have the wrong default boolean in the first place.
I assume the starting resource is R.drawable.role_button.
Change isPressed to false and put it into your clickListener, because it's not pressed right?
And I suggest you to use a better shortcut in your if else
rolebutton.setOnClickListener(new View.OnClickListener() {
boolean isPressed = false;
public void onClick(View v) {
rolebutton.setBackgroundResource(isPressed ? R.drawable.role_button : R.drawable.role_button_pressed));
isPressed = !isPressed;
}
}
In an android application I am implementing an Animation on an ImageButton. After the animation the button remains at it's last frame but it is only clickable at it's initial position, I have Implemented an BounceInterpolation animation by these line of code in my main .java Activity
TranslateAnimation translation;
translation = new TranslateAnimation(0f, 0F, 0f, 200);
translation.setStartOffset(150);
translation.setDuration(100);
translation.setFillAfter(true);
translation.setInterpolator(new BounceInterpolator());
mTourButton.startAnimation(translation);
I don't know how to update the the ImageButton parameters. Most of the solutions are for the xml implemented animations. I didn't find any solution. I am tired now please help.
Try to use onTouch() to handle your clicks.
imageButton.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction())
{
case MotionEvent.ACTION_UP :
{
// Do whatever you want here.
}
return true;
}
});
That should do it.
Better add onClick in XML:
<ImageButton
android:onClick="doSomethingMethod"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageButton"
android:layout_above="#+id/imageView"
android:layout_centerHorizontal="true" />
Press ALT+ENTER on doSomethingMethod and Create 'doSomethingMethod(View)' in 'Activity'
This will create a method like onClickListener in your Activity.
Hope it helps!
I started program little bit in android,
I have 3 buttons in a single activity.
I saw some example codes that assign the same OnClick event to all the buttons (even if they perform completely different action) and in the method Switch(id) case case case...
What is the better approach? one onClick method and switching or a lot of methods, one for each button?
Thanks.
Use this way:
#Override
public void onCreate(Bundle savedInstanceState) {
button1.setOnClickListener(onClickListener);
button2.setOnClickListener(onClickListener);
button3.setOnClickListener(onClickListener);
}
private OnClickListener onClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.button1:
//DO something
break;
case R.id.button2:
//DO something
break;
case R.id.button3:
//DO something
break;
}
}
};
If you want to reduce the coding lines then use View's OnClick() with switch statement and if you want to handle separately all click (for easily understanding and maintaining code) then use separate all button's onClick().
Update:
If you have declared Buttons in your Activity layout xml file, than write attribute android:onClick="" with same method name for all buttons and implement that method in your activity. Now you have one method for all buttons and in that method differentiate buttons with id.
Example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button android:id="#+id/button1"
android:layout_width="wrap_content"
android:onClick="buttonOnClick"
android:layout_height="wrap_content"
android:text="Hello, I am a Button 1" />
<Button android:id="#+id/button2"
android:layout_width="wrap_content"
android:onClick="buttonOnClick"
android:layout_height="wrap_content"
android:text="Hello, I am a Button 2" />
<Button android:id="#+id/button3"
android:layout_width="wrap_content"
android:onClick="buttonOnClick"
android:layout_height="wrap_content"
android:text="Hello, I am a Button 3" />
</LinearLayout>
Now in your Activity implement buttonOnClick like,
public void buttonOnClick(View view)
{
switch(view.getId())
{
case R.id.button1:
// Code for button 1 click
break;
case R.id.button2:
// Code for button 2 click
break;
case R.id.button3:
// Code for button 3 click
break;
}
}
Or you can apply same switch case for dynamically added buttons in your activity,
like instead of buttonOnClick you have to use implemented View's OnClickListerner's onClick.
this.btnAddFriedtoFacebook = (Button) this.findViewById(R.id.btnAddFriedtoFacebook);
this.btnAddFriedtoFacebook.setOnClickListener(this.backButtonClickListener);
public OnClickListener backButtonClickListener = new OnClickListener()
{
public void onClick(final View view)
{
if (view == MatchInfoActivity.this.btnBack)
{
MatchInfoActivity.this.finish();
}
if( view == MatchInfoActivity.this.btnAddFried){
Intent i = new Intent(Intent.ACTION_VIEW);
MatchInfoActivity.this.startActivity(i);
}
if( view == MatchInfoActivity.this.btnAddBuddy){
Intent i = new Intent(Intent.ACTION_VIEW);
MatchInfoActivity.this.startActivity(i);
}
}
};
Here is the good way.
I think registering onClick in xml (layout) is better approach.
EDIT:
Found related threads :
Best practice for defining button events in android
best practices for handling UI events
Registered onClick event in the XML layout and then handle it in the code. This is how I would do it:
<Button
android:id="#+id/btplus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click me"
android:onClick="onBtnClicked">
Method in .class
public void onBtnClicked(View v) {
switch (v.getId()) {
case R.id.btplus:
Toast.makeText(getApplicationContext(), "Plus is clicked" + "+", Toast.LENGTH_SHORT).show();
break;
case R.id.btminu:
Toast.makeText(getApplicationContext(),"Minus is clicked" + "-", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
Little addition to #Nguyen answer.
findViewById(R.id.buttonOne).setOnClickListener(buttonClickListener);
.... .... .... ....
findViewById(R.id.buttonN).setOnClickListener(buttonClickListener);
private View.OnClickListener buttonClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonOne:
// do something
break;
.... .... .... ....
case R.id.buttonN:
// do something
break;
}
}
};
This could be useful, if you don't want to initialize the button variable, but want to track button click event. Thanks!
Here is my problem. I setup the buttons exactly the way they are setup in the Android documentation, but I am getting a warning, and the button will not do anything.
Here is my Java code:
package com.variDice;
import android.app.Activity;
import android.os.Bundle;
import android.widget.*;
public class VariDiceActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//die1Clicked();
}
private void die1Clicked() {
ImageButton die1button = (ImageButton)findViewById(R.id.die1button);
die1button.setImageResource(R.drawable.icon);
}
}
...and the XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:weightSum="1" android:layout_gravity="center_horizontal">
<ImageView
android:id="#+id/varidice_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="#drawable/icon"></ImageView>
<ImageButton
android:id="#+id/die1button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#null"></ImageButton>
</LinearLayout>
...and the warning:
The method die1Clicked from the type VariDiceActivity is never used locally.
I must say that I am completely new to Android development. I made my app for the iPhone, and I am now trying to make a version for the android. The iPhone version was sooo much easier, because of the better interface builder (so I can just make an action and connect it to the button that way), so this is almost impossibly hard for me to understand. In other words, I do not understand how you connect an action to the button. Could somebody please tell me what I am doing wrong?
Try this in your xml:
<ImageButton
android:id="#+id/die1button"
android:onClick="die1Clicked"
...></ImageButton>
And in your code, change the method signature to:
public void die1Clicked(android.view.View v) {
ImageButton die1button = (ImageButton)findViewById(R.id.die1button);
die1button.setImageResource(R.drawable.icon);
}
Here is the Android Button tutorial.
To bind some behavior to an UI button, you need to register a listener that receives notifications of a certain event type. In your case, you register a OnClickListener (for the click event); just like in the following snippet:
// create the implementation of OnClickListener
private OnClickListener mDie1Listener = new OnClickListener() {
public void onClick(View v) {
// do something when the button is clicked
}
};
protected void onCreate(Bundle savedValues) {
...
// get the button from layout
Button button = (Button)findViewById(R.id.die1button);
// register the onClick listener with the implementation above
button.setOnClickListener(mDie1Listener);
...
}
You need to add a click listener to your button. Put this in your onCreate():
ImageButton die1button = (ImageButton)findViewById(R.id.die1button);
die1button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// What to do when the button is clicked
});
Most answers on SO tend to use 'setOnClickListener' instead of using xml properties.
I personally prefer using xml for making items clickable in android.
The mistake you have made is setting your function as private. The function which gets called after clicking the item should be public.
There are 3 things you should keep in mind:
Define the 2 properties in xml.
android:clickable="true"
android:onClick="functionName"
Define that function in the Activity file. Make sure to keep the function public.
public void functionName(View v) {
// TODO Auto-generated method stub
}
Make sure to pass 'View v' as an argument for that function.