Android TextView issue in run() - java

class BTCSync extends Thread{
public void run(){
while(!BTC && MainPage.BTC){
TextView BTCPer = (TextView) findViewById(R.id.lblBTCPer);
BTCPer.setText(BTCProgress+"%");
if(BTCProgress == 100) {
BTCPer.setText("100%");
BTC = true;
}
}
}
}
The error is where findViewById my label is lblBTCPer.
The reason I have it in run() is that this block needs to run until the value hits 100.
I know that usually, you would have to throw in the View v but then it would negate the void run().
I looked for a few solutions but I haven't found a working example.
I also believe I posted this already just yesterday but I can't seem to find it anywhere. It's not under my account and I distinctly remember posting it and waiting for responses.

Aside from the findViewById problem, you're trying to do UI work on a non-UI thread. You'll need to use runOnUiThread() or you're going to get a crash:
runOnUiThread(new Runnable() {
#Override
public void run() {
BTCPer.setText(BTCProgress+"%");
if(BTCProgress == 100) {
BTCPer.setText("100%");
BTC = true;
}
}
});

findViewById() is a function of either Android Activity or View. Your BTCSync class does not extend neither of those.
Most likely Timertask or Handler would be a better fit for your requirement.

Android app have only one UI thread, and the Android UI toolkit is not thread-safe and must always be manipulated on the UI thread.
In order to update ui in other thread, you could use handler, here is ref
and here is a good example

Android forbids altering the UI (for example setting a text of a textview) outside of the UI thread.
If you are able to call this line
TextView BTCPer = (TextView) findViewById(R.id.lblBTCPer);
then that means that your thread is defined inside your activity, because findViewById() is not a Thread's function. In this case:
class BTCSync extends Thread{
public void run(){
while(!BTC && MainPage.BTC){
TextView BTCPer = (TextView) findViewById(R.id.lblBTCPer);
// make sure you enable lambdas
runOnUiThread(() -> BTCPer.setText(BTCProgress+"%"));
if(BTCProgress == 100) {
BTCPer.setText("100%");
BTC = true;
}
}
}

Related

How do I change image and after rerunning the main activity in the thread after 5 sec not go back to the basic layout in android

So I am creating an app where I get the string from my AsyncTask which I have created as a subclass in my MainActivity class in order to get the variables I receive from the internet. And according to variables I get I change the images accordingly after every 5 seconds. Now the task is successful but on activity refresh I keep getting the default layout I created in activity_main.xml and again it changes to the one I want.
Posting my code below.
Thread thread = new Thread() { //thread I am running every 5 secs
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
syncapp sa = new syncapp(); //AsyncTask to get String from Internet
sa.execute();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(), MainActivity.class); //Creating intent to restart the activity (Need a workaround if possible)
startActivity(mainActivity);
}
;
};
thread.start();
}
public void setImage(String str) //Function I will call to change Image
{
vacancy =0;
b = a.toCharArray();
for (int i = 0; i < 6; i++)
if (b[i] == '0'){
iv[i].setImageResource(R.drawable.img2);
vacancy++;}
else if (b[i] == '1') {
iv[i].setImageResource(R.drawable.img1);
}
Log.i("abc ", a);
tv.setText("InBackGround" + str);
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* for(int i =0;i<6;i++) {
outState.putChar("imv"+(i+1), b[i]);
}*/
outState.putString("a",a); //Getting a from the internet (php file)
Log.i("Saved State", "Activity A - Saving instance state");
}
Now what I want is if you have a better method to do this thing. Eg. In a Stock market application the prices keep on changing. The same way I want my image to change according to the data I get.
If this is the only method then how do I save changes I make
(eg. like in the code above setImageResource to img2) permanently.
If I can use something else ImageView.
I have already used onSaveInstanceState But as I am taking values from the internet I don't know I am not able to use them.
So first of all.. when working with UI elements such as Views, Widgets, you would want to avoid spawning your own threads, as View can be touched only from the thread it was created from.
Second.. you would also want to avoid sleeping inside your worker thread - basically just use the Handler class (from android.os) and post a delayed Runnable, like so: https://stackoverflow.com/a/20784353/2102748. Just be sure to stop all work on that particular handler when your Activity stops, like so: https://stackoverflow.com/a/3627399/2102748
Third - you should perhaps load the photo immediately (on the same AsyncTask or thread) if that is the only thing you need, but be sure to post the "set bitmap to view" work to the handler you created.
Hope this helps.

Call a method inside a fragment when all UI elements are visible

Is there a way of calling a method inside a fragment once all UI elements are displayed? I am having trouble at the moment trying to figure out which method in the fragment lifecycle should I put my code in. Basically, I am waiting for some input on my socket by calling inStream.read(buffer) where inStream is an inputStream and buffer is a byte[] already initialised. My problem is that this call prevents the UI from being fully displayed whether I call it in onResume, onStart, onActivityCreated (it always waits until it has received something before the UI is fully displayed). Is there a specific fragment lifecycle method for this? (So the socket kinda works in the background just waiting for input instead of hogging the foreground, since the data received will be used to fill some textboxes in the UI of the fragment).
You are blocking the UI Main thread who is responsible for drawing UI components, open and read your Socket in a separeated Thread like AsyncTask
sockeqwe is spot on, you should perform that task in a separate thread so as not to block the UI thread.
To answer the question in the title in case anybody arrives here for that reason, a global layout listener can be used to get a callback when layout is complete. In onCreateView use any view in your layout:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// do whatever you need
// remove the layout listener if not needed anymore
removeOnGlobalLayoutListener(view, this);
}
});
Here's a static helper method to remove the layout listener
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener victim) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
removeLayoutListenerJB(v, victim);
} else removeLayoutListener(v, victim);
}
#SuppressWarnings("deprecation")
private static void removeLayoutListenerJB(View v, ViewTreeObserver.OnGlobalLayoutListener victim) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(victim);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static void removeLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener victim) {
v.getViewTreeObserver().removeOnGlobalLayoutListener(victim);
}

Call a method when fragment is visible to user

I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck

Android animation only shows the last ArrayList element

I have 5 TextView fields that are meant to display a String and change the text every 10 seconds.
In order to make the change I have an AsyncTask that grabs the next 5 messages (doInBackground) and passes them to onPostExecute to be displayed in the UI.
When I display the messages without an animation, everything works perfectly fine and the messages rotate every 10 seconds via a new AsyncTask every time.
Now, when I attempt to use a simple fade-in/out animation on the TextViews, I can see all of them fading out and then back in with a placeholder string however the actual text messages I want to display are only displayed in the last TextView.
The AsyncTask is an inner-class of my MainActivity.java.
I have implemented an AnimationListener in MainActivity.java and due to that the objects below in onPostExecute ((TextView) mNextTextView & (String) mNextMessageToDisplay) are also declared as global variables in the main class.
Here's AsyncTask's onPostExecute:
protected void onPostExecute(List<String> mFiveMessagesToDisplay) {
super.onPostExecute(mFiveMessagesToDisplay);
for (int mTextViewCounter = 0; mTextViewCounter < 5; mTextViewCounter++) {
// Prepare the next TextView and message to use
mNextTextViewToUse = mTextViewArrayList.get(mTextViewCounter);
mNextMessageToDisplay = mFiveMessagesToDisplay.get(mTextViewCounter);
// Post and animate the message changes in the TextView
mNextTextViewToUse.startAnimation(mFadeOutAnimation);
}
}
And my AnimationListener:
private AnimationListener mFadeOutAnimListener = new AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
mNextTextViewToUse.setVisibility(View.INVISIBLE);
mNextTextViewToUse.setText(mNextMessageToDisplay);
mNextTextViewToUse.setVisibility(View.VISIBLE);
mNextTextViewToUse.startAnimation(mFadeInAnimation);
}
}
As you can see, my setText() method is applied on the mNextTextViewToUse object with the contents of mNextMessageToDisplay, which are as mentioned before both global variables in the main class.
Both ArrayLists, the one containing the messages and the other containing the correct, I have iterated over both and printed the contents - everything is in its place.
Reminding again that if I use setText() with the same TextView and String objects under onPostExecute, everything works perfectly fine just without the animation.
Many thanks in advance!
Your issue is that the loops finishes by the time the animation ends, that's why you see only the last message.
Lets say that mNextTextViewToUse points to TextView1. You start a fade out animation for this TextView. By the time the animation ends, mNextTextViewToUse points to a different textView object (because the loop continue to run while the fade out animation is playing) so when onAnimationEnd is being called (by the fade out animation of TextView1) mNextTextView points to TextView5, and basically you are setting the text to this text view only.
There are several ways to solve this / implement what you need, personally I would probably create my own TextView class (extending TextView of course) and I'll add a public method called setTextAnimated (String newText). It will look something like:
public void setTextAnimated (String newText) {
mNextText = newText;
startAnimation(mFadeOutAnim);
}
where your onAnimationEnd callback will simple call setText(mNextText) and then will start fade-in animation.
Using this you will avoid another issue - that if you use the same animation object on different views - they will share the same timeline and interpolator. The result is that if View1 start a fade out animation that should take 200ms, and View 5 starts the same animation after 140ms, View5 will immediately "jump" to the same alpha value that View1 is.
I found the solution!
I ended up completely removing the FOR loop logic and using recursion which raises a Boolean flag once it has used all 5 available TextViews.
The method that gets the next TextView and String message and execute the animated setText():
public void updateTextView(List<String> mMessages) {
mNextTextViewToUse = mTextViewArrayList.get(mCurrentMessageIndex);
mNextMessageToDisplay = mMessages.get(mCurrentMessageIndex);
mNextTextViewToUse.startAnimation(mFadeOutAnimation);
if (mCurrentMessageIndex == 4) {
mStopMessageDisplayRepeat = true;
} else {
mCurrentMessageIndex++;
}
}
The Runnable that calls the method above, while the recursion stop Boolean is false:
public Runnable mUpdateTextViewRunnable = new Runnable() {
final static int POSTDELAY_INTERVAL = 600;
#Override
public void run() {
updateTextView(mFiveMessages);
if (! mStopMessageDisplayRepeat) {
mMessageDisplayRepeatHandler.postDelayed(mUpdateTextViewRunnable, POSTDELAY_INTERVAL);
}
}
};
Execute the Runnable:
public void startTextViewRunnableRepeat() {
mUpdateTextViewRunnable.run();
}
This is how my AsyncTask's onPostExecute() looks like:
#Override
protected void onPostExecute(List<String> mFiveMessagesToDisplay) {
super.onPostExecute(mFiveMessagesToDisplay);
mFiveMessages = mFiveMessagesToDisplay;
startTextViewRunnableRepeat();
}

Update Textview from function Android

someone can tell me how to update a control Textview Android from a function? I have searched deep into the internet and see many people who ask the same question, I tested threads but could not work, someone has a simple working example of doing this? for example to call a function (which runs several times in a loop) and the function writes in the TextView, but the problem is that until the function is not finished running, it shows me the text.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
while(condition) //here freezes the UI and the text in textview only shows when the loop ends
{
HardWork();
}
}
public void HardWork() {
txtProgreso.append("Test Line" + "\n\n");
};
Thanks in advance.
If i understood you correctly
Use AsyncTask here, So you can update textview in onProgressUpdate method
private class SomeTask extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
while(condition) {
//do more
publishProgress("Some text value to you textview");
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
txtProgreso.append(values[0]);
}
}
I think what you're trying to ask is "how do I force my TextView to show its updated contents without returning control to the runloop?" (ie, returning out of your function).
If so, I'm afraid you're out of luck, such a thing is impossible to do with Android's UI model. The nearest thing to this that is possible is to keep track of your loop state, and set a timer that will call a function for you to update the TextView.
Here's an example. Suppose you want to put the text "this is a test" into a TextView, one character at a time:
private Handler mHandler = new Handler();
private String desiredText = new String("This is a test");
private Runnable mUpdateTextView = new Runnable() {
public void run() {
TextView textView = (TextView)findViewById(R.id.myTextView);
int lengthSoFar = textView.getText().length();
if (lengthSoFar < desiredText.length()) {
mTextView.setText(desiredText.substring(0, lengthSoFar + 1));
mHandler.postDelayed(100, mUpdateTextView);
}
}
};
protected void onStart() {
mHandler.postDelayed(100, mUpdateTextView);
}
I think some of my object visibility is incorrect here, and I don't have an Android environment handy to test on, but you get the general idea. Rather than a Handler you could also use a Timer, but it's a bit more heavyweight so it depends how often you want to be updating the UI.
You get the textview by doing the following.
TextView myTextView = (TextView)findViewById(R.id.textViewsId);
Then afterwards, you can change the TextView using the functions. For example, you can set the text using the following code.
myTextView.setText("New text example");
Are there any other updates to the control you are trying to do?

Categories

Resources