Nasreddine Nasreddine - 2 months ago 17
Android Question

Listeners don't work in OnUiThread

so i had a AsyncTask with RunOnUiThread to interactively change ui elements and add TableRows the problem is on the listeners in TableRows. only one works but the others don't. this is a simple from my code

public class SearchAsync extends AsyncTask<String, String, String> {
private Context mContext;
private Activity mAct;

@Override
protected void onPreExecute() {
super.onPreExecute();

}

public void mytask(Context context, Activity act) {
mContext = context;
mAct = act;
}

@Override
protected String doInBackground(final String... arg0) {
String sc2 = "Alpha";

mAct.runOnUiThread(new Runnable() {
@Override
public void run() {
GridLayout gey = (GridLayout) mAct.findViewById(R.id.greylayout);
TableLayout tbly = (TableLayout) mAct.findViewById(R.id.listviewtable);{
final TableRow tbro = new TableRow(mContext);
TextView tev = new TextView(mContext);
tbro.setBackground(mAct.getResources().getDrawable(R.drawable.mybgbg));
tev.setTextColor(Color.WHITE);
tev.setText(sc2);
tbro.addView(tev);
tbro.setOnClickListener(new View.OnClickListener() { // this listener doesn't work.
public void onClick(View v) {
System.out.println("yey! it works");
}
});

tbro.setOnLongClickListener(new View.OnLongClickListener(){ // this listener doesn't work too.
@Override
public boolean onLongClick(View v){
System.out.println("yey! it works too!");
return true;
}
});
tbro.setOnTouchListener(new View.OnTouchListener() { // this listener works perfectly.
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
tbro.setBackground(mAct.getResources().getDrawable(R.drawable.mybgi));
} else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
tbro.setBackground(mAct.getResources().getDrawable(R.drawable.mybgbg));
}
return true;
}
});
tbro.setId(R.id.layoutx1);
tbro.setLongClickable(true);
tbro.setClickable(true);
tbly.addView(tbro);

}
}
}

TableRow tete = (TableRow)mAct.findViewById(R.id.layoutx1);

});
}
catch(JSONException e){
Log.e(TAG, "Json parsing error: " + e.getMessage());
);

}
}
return null;

}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(null);
final String rslt;
rslt = result;
mAct.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(mContext, rslt, Toast.LENGTH_SHORT);
toast.show();
}
});

}


So only OnTouchListener works but the others don't.. and i don't know why.
this is only a problem in runOnUiThread and runonmainthread.

i tried the code onPreExecute() method and also onPostExecute. nothing works.

but in the MainActivity it works just fine.

Thanks for reading my question.

Answer

This is happening because you are returning true from onClick(), which indicates to the system that you have "consumed" the event.

View.OnTouchListener | Android Developers

The touch event would happen before the click event, because an ACTION_DOWN would trigger a touch event, but you need to have an ACTION_UP also before you can have a click event.

When you return true from onTouch(), this consumes the touch event, preventing the ACTION_UP from triggering the click event.

As a general guideline, you shouldn't use OnTouchListener and OnClickListener together.

It looks like you are using the OnTouchListener only to change the background. A StateListDrawable would be much better for that. You set the background once, and the state list takes care of the rest:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@drawable/mybgi"
        android:state_pressed="true"/>
    <item
        android:drawable="@drawable/mybgbg"/>
</selector>

State List | Drawable Resources | Android Developers