My problem is pretty simple. I am creating a card based on the result of a HTTP query performed inside a separate thread. The card also has an onclick method and is defined inside a runOnUiThread() located inside the separate thread. However, when the device is tapped, the onclick event isn't fired.
Here is my code:
private void login() {
Runnable r = new Runnable() {
#Override
public void run() {
// irrelevant code
runOnUiThread(new Runnable() {
#Override
public void run() {
setContentView(buildError(code));
}
}
}
Thread t = new Thread(r);
t.start();
}
private View buildError(String code) {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.ALERT);
card.setIcon(R.drawable.ic_warning_150);
if (code.equals("1"))
card.setText("Incorrect credientals");
else
card.setText("Unexpected error");
card.setFootnote("Tap to try again");
View cView = card.getView();
cView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Event", "Clicked"); // This is what isn't triggering
}
});
cView.setFocusable(true);
cView.setFocusableInTouchMode(true);
return cView;
}
Even though the snippet of code contains an error (can't be compiled, missing ; at the Runnable statement), you were on the right track.
The View simply needs to request the focus in order to be clickable right away. Otherwise you'll have to move the focus manually.
cView.setFocusable(true);
cView.setFocusableInTouchMode(true);
cView.requestFocus();
Reference
Related
Is it possible to generate a TextView inside a for loop in the main activity? I have to run a thread from another Java class "multithread." When this class runs, I will know how many clients connected to, I will run the insider thread to generate TextViews according to the number of the clients, and display the received messages in these TextViews.
But I am obtaining an error. If you know any better way to run a thread inside the main activity, please let me know, thanks.
Here is the method:
public void toggleButtonConnectToClientsFunction(View view) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
//Multithread here
multicastthreadRun.run();
for(int counter=0;counter<multicastthreadRun.ClientIpArrayList.size();counter++) {
TextView textView=new TextView(this);//i am obtaining error here
linearLayoutSecondaryTexts.addView(textView);
runOnUiThread(new Runnable() {
#Override
public void run() {
//in this case we can change the user interface
}
});
}//end of the for loop
}
});t.start();
new Runnable is an anonymous class and this is pointing to that anonymous class. To create a textview, you need to pass context (either activity or context) so in order to do that, use specific reference to class with className as
TextView textView=new TextView(YourContainerActivityClassName.this);
// e.g TextView textView=new TextView(MainActivity.this);
Note: since it's a click listener so seems like this is in an activity directly
and you cannot update the UI from worker threads so do it like
public void toggleButtonConnectToClientsFunction(View view) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
//Multithread here
multicastthreadRun.run();
for(int counter=0;counter<multicastthreadRun.ClientIpArrayList.size();counter++) {
// use proper context
TextView textView=new TextView(YourActivityNamethis);//i am obtaining error here
runOnUiThread(new Runnable() {
#Override
public void run() {
// Update UI
linearLayoutSecondaryTexts.addView(textView);
}
});
}//end of the for loop
}
});t.start();
}
TextView textView = new TextView(this);//i am obtaining error here
TextView textView = new TextView(getBaseContext());// textview expect context object NOT runnable object.
And instead of using loop you can just use Recursion.
public void toggleButtonConnectToClientsFunction(View view) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
//Multithread here
multicastthreadRun.run();
makeTextView ( 0,multicastthreadRun.ClientIpArrayList.size());
}
});
t.start();
}
private void makeTextView(final int count, final int max){
if (count>= max)
return; // end of Loop
TextView textView = new TextView(getBaseContext());// Use can you this here as it will refer to your activity object.
runOnUiThread(new Runnable() {
#Override
public void run() {
//in this case we can change the user interface
linearLayoutSecondaryTexts.addView(textView);
makeTextView(count+1, max);
}
});
}
In your code TextView(this) will not work because it is inside on Thread.
You have to specify the context exactly. Check below code:
TextView tv = new TextView(YOUR_ACTIVITY.this);
tv.setText("What to do");
Thanks :)
I am using a RecyclerView that show results that come from GCM callbacks. The RecyclerView has a custom adapter a method add, there is also a progress bar that updates using an asynctask.
Message recieving over GCM that works fine:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.add(new ResultRecord("asf", 89, 1000));
}
});
}
};
Add method in the custom adapter:
public void add(final ResultRecord result) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
results.add(0, result);
notifyItemInserted(0);
}
});
}
The problem is that the method add called and nothing happens on the UI. The method add called and then onBindViewHolder and the recycler view does not update. Only when the progress bar is finished the RecylcerView is getting update with all the ViewHolders that has been added before.
I have checked if the add method works from the onCreate method and it worked fine. Maybe this problem is related to threading.
You have a Threading problem here.
Your code is based on ArrayList, which isn't Thread-Safe. You are calling the "Add" method from event, which called probably from multiple threads.
You have to synchronize your code. Something like this:
private final ReentrantLock lock = new ReentrantLock();
public void add(final ResultRecord result) {
lock.lock();
try {
AddNotThreadSafe(result); // Only one thread add in same time. Now is safe for executing.
} finally {
lock.unlock();
}
}
Now, move your original Add code to separated method called AddNotThreadSafe.
This should work. :)
I have tried so many ways of solving my problem, but still no success.I have a method, which returns me a string value and I am using it to update TextView on my screen like this:
outCPU.setText(getCpuInfo());
Which would be fine, but I need to update this TextView until back button was pressed.
I guess i have need a while loop which starts after activity has been created and stops after back button was pressed. This loop should be in a new thread, because:- I have to load the activity first and execute the loop in another thread so the executing won't affect main thread and loading of the activity.
As I've already said, I don't know how to do this properly even though i have spent few hours on it.
Could someone show me an example how to get this done? Thanks...!!
EDITED - WORKING:
private Handler mHandler;
private int i;
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_systeminfo);
outCPU = (TextView) findViewById(R.id.outCPU);
outMEM = (TextView) findViewById(R.id.outMEM);
outTASKS = (TextView) findViewById(R.id.outTASKS);
i = 0;
mHandler = new Handler();
mHandler.post(mUpdate);
}
private Runnable mUpdate = new Runnable() {
#Override
public void run() {
outCPU.setText(getCpuInfo());
outMEM.setText(getMemInfo());
outTASKS.setText(getTasksInfo());
i++;
mHandler.postDelayed(this, 1000);
}
};
#Override
public void onBackPressed() {
mHandler.removeCallbacks(mUpdate);
super.onBackPressed();
Log.i("MSG", "Going back");
finish();
}
You can use AsyncTask to perform operations on UI Thread while being in a Thread. Or you can use 'my favorite' , the combination of Thread and Handler. To make sure the thread is stopped when back is pressed, you can use handler.removeCallBacks(Runnable) The following example could solve your problem:
//Global
Handler h = new Handler();
private static boolean flag = true;
public void updateTextView(){
// call thread here
h.post(thread);
}
// take this thread out side so that it can be stopped with handler
Thread thread = new Thread(){
public void run(){
while(flag)
outCPU.setText(getCpuInfo());
}
}
public void onBackPressed(){
flag = false;
h.removeCallBacks(thread);
super.onBackPressed();
}
Use a shared flag somewhere in your app:
private volatile boolean wasPressed = false;
In while loop, check this flag:
while (!wasPressed) {
runOnUiThread(new Runnable() {
#Override
public void run() {
outCPU.setText(getCpuInfo());
}
});
// sleep for a while
}
On button click listener, switch wasPressed to true.
I've seen some similar questions and got some information but they stop shy of telling me enough to get it working.
What I'm trying to do is make a simple rhythm game where the player taps a button at regular intervals (ie. beats). I wanted to set up a way of signalling when to tap by having the button change colour, and since this would be a repeated task at regular intervals I want to use a timer object with a schedule method.
But when I try calling on this method it tells me that I can't change the UI in a non UI thread. I've tried a few ways to write a method in the main thread that I can call from the timer object but I get the same error every time. I'm assuming that I just have the wrong idea about what counts as being from the UI thread, so I was hoping someone could clear it up.
Here's a snippet of one way I tried it, just to show what my code looks like:
OnClickListener clickButton = new View.OnClickListener() {
public void onClick(View v) {
if (startBeat == 0){
startBeat = System.nanoTime();
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
flashButton();
}
}), 0, beatTime);
timerEnd.schedule(new TimerTask()
{
public void run()
{
unflashButton();
}
}, beatTolerance*2, beatTime);
return;
}
};
public void flashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
public void unflashButton(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
To be clear, this is all contained within my MainActivity class along with the OnCreate class.
if you are in an activity all you need to do is use runOnUiThread() and then place the code to change the ui element in there
public void flashButton(){
runOnUiThread(new Runnable() {
#Override
public void run() {
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
You cannot, under any circumstances, touch a UI object from a non UI thread.
You can accomplish your intent using Handler.sendMessageDelayed
UI can only be touched by the main thread. You should post the actions you are performing on the ui thread via handler or via runOnUiThread
Try something similar to this
timerStart.scheduleAtFixedRate((new TimerTask()
{
public void run()
{
//replace MainActivity with your activity
//if inside a fragment use getActivity()
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
flashButton();
}
});
}
}), 0, beatTime);
If you are in an Activity you could surround flashButton() with an runOnUiThread.
...
runOnUiThread(new Runnable(){
public void run(){
flashButton();
}
});
...
use android.os.Handler Class. Change your code as follows:
private Handler handler=new Handler();
public void flashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1transparent_very));
}
});
}
public void unflashButton(){
handler.post(new Runnable(){
public void run(){
beatPrompt.setBackgroundColor(getResources().getColor(R.color.primary1));
}
});
}
So I have some simple code but it seems to not be working.. any suggestions?
I just want an image to show after a button is pressed then become invisible after 2 seconds.
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// delay of some sort
firstImage.setVisibility(ImageView.INVISIBLE);
}
}
The image never shows, it always stays invisible, should I be implementing this in another way? I've tried handlers.. but it didn't work, unless I did it wrong.
Never make your UI thread sleep!
Do this:
final Handler handler = new Handler();
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
handler.postDelayed(new Runnable(){
public void run(){
firstImage.setVisibility(ImageView.INVISIBLE);
}
}, DELAY);
}
}
Where you would set DELAY as 2000 (ms).
Well, you will need to add a delay between the two lines. Use a thread or a timer to do this.
Start a thread on click of a button. In the run method, change the ImageView's visibility to VISIBLE, then put the thread to sleep for n secs, and then change then make it invisible.
To call the imageView's setvisibility method, you will need a hanlder here.
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
image.setVisibiliy(VISIBLE);
Thread.sleep(200);
image.setVisibility(INVISIBLE);
}
});
I know this question has already been answered, but I thought I would add an answer for people who like me, stumbled across this looking for a similar result where the delay was caused by a process rather than a "sleep"
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
firstImage.setVisibility(ImageView.VISIBLE);
// Run the operation on a new thread
new Thread(new Runnable(){
public void run(){
myMethod();
returnVisibility();
}
}).start();
}
}
private void myMethod() {
// Perform the operation you wish to do before restoring visibility
}
private void returnVisibility() {
// Restore visibility to the object being run on the main UI thread.
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
firstImage.setVisibility(ImageView.INVISIBLE);
}
});
}