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?
Related
So I understand the basics of java programming but when I'm trying to use my little knowledge in android studio it make everything harder having classes and different files needing to be referenced. Coming from python, when making a simple game I would define different functions, then run them in a game loop like
while running:
or something similar. I know to define something in java you go like
public void Example() {}
but when I use this in java, when I try to run the program my game either instantly crashes or doesnt load anything.
The code at the moment is
public class MainActivity extends AppCompatActivity {
//Variables
Boolean running = true;
public int years = 0;
//Setup Year Counter
TextView textView = (TextView) findViewById(R.id.year_counter);
//Advance Button
public void advance() {
ImageButton button = (ImageButton) findViewById(R.id.advance);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
years += 1;
textView.setText("" + years + "");
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Game Loop
while (running) {
advance();
}
}
}
And this results in the app not opening.
Any help at all would mean a lot to me.
Thanks in advance :)
Although I don't actually see a crash, since you didn't really upload one, I can see why you might think your app wont work.
What you are doing constantly in the while loop is that you are only setting the button's click listener over and over again.
public class MainActivity extends AppCompatActivity {
//Variables
Boolean running = true;
public int years = 0;
//Setup Year Counter
TextView textView;
ImageButton button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// you set up your views once, after the layout is inflated
setUpViews();
// you initialize your buttons functionality
initClickEvents();
//Game Loop
while (running) {
// do other stuff
}
}
private void setUpViews() {
textView = (TextView) findViewById(R.id.year_counter);
button = (ImageButton) findViewById(R.id.advance);
}
private void initClickEvents() {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
years += 1;
textView.setText("" + years + "");
}
});
}
}
I'd recommend working with a game engine.
If you want to stick with java, libGDX is an option.
But if language (and IDE) doesn't matter, than a better option is Godot. The reason why I recommend Godot over some of the more popular game engines is because it's open source, 100% free and plus GDScript (godot's scripting language) is heavily influenced by python.
If you want to make you own game engine in java check out this: https://java-design-patterns.com/patterns/game-loop/#
Keep in mind that the while loop is calling the advance method. So every loop you are setting the view for button and then setting an OnClickListener for it. Not everything needs to be in the while loop.
You must implement (override) the render method (it is called in the game loop).
I suggest trying a complete example in the documentation.
``
#Override
public void render() {
ScreenUtils.clear(0, 0, 0.2f, 1);
advance();
...
``
this is my first question on stack (great community, thanks!)
So, I have this problem:
I'm developing an android application with a tab layout. Whenever I navigate to the "Status" tab, I'd like to show a circular ProgressBar that is updated according to the value of a variable. Let's call this variable prog. Moreover, I'd like to display in the center of the ProgressBar a TextView that displays the same value of prog.
I have came up with this part of code:
public class StatusFragment extends Fragment
{
private ProgressBar torqueRefProgressBar;
private TextView torqueRefTextView;
RunningThreadExample r;
private Thread t1;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate the layout for this fragment
return ...;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
// Initializations and other parts not relevant for the question...
this.torqueRefProgressBar = getView().findViewById(R.id.torqueRefProgressBar);
this.torqueRefTextView = getView().findViewById(R.id.torqueRefTextView);
t1 = new Thread( this.torqueRefProgressBar, this.torqueRefTextView)); // passing arguments by value but values are references to the objects, therefore I'm passing by ref
t1.start();
}
}
In this Fragment I create the ProgressBar and the TextView and then pass them to the Runnable as you can see below
public class RunningThreadExample implements Runnable
{
ProgressBar torqueRefProgBar_thread;
TextView torqueRefTextView_thread;
public RunningThreadExample(ProgressBar progressBar, TextView textView)
{
this.torqueRefProgBar_thread = progressBar;
this.torqueRefTextView_thread = textView;
this.endScale = this.torqueRefProgBar_thread.getMax();
}
#Override
public void run()
{
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
float i = 0;
double temp = 0;
while(Running)
{
try
{
Thread.sleep(1000/60); // update freq 60Hz
}
catch (InterruptedException e)
{
e.printStackTrace();
}
temp = getCurrentVariableValue(); // Not shown here (returns a double)
this.torqueRefProgBar_thread.setProgress((int) temp);
this.torqueRefTextView_thread.setText(String.valueOf(temp));
}
Log.i(INFO_TAG,"Finishing thread!!!!!");
}
}
I'd like this update to run all the time the app is working (for this reason I've neglected AsyncTask). After a while, it happens that the ProgressBar stops updating, while the TextView continues to work without any problem.
I've been reading that a possible cause could be that the calling setProgress(temp) might stuck the UI. However I can't understand while the TextView is still working.
Can you suggest a better way to make the progressBar update?
Thank you!
See this thread:
Android basics: running code in the UI thread
So to summarize, Android requires certain types and sections of code to run in a specific context. Many tools such as Handlers are available to help with these tasks but it was difficult to learn to begin with.
You cannot affect View from any Thread other than Main Thread. Do the following in run() method
runOnUiThread(new Runnable() {
//
run(){
this.torqueRefProgBar_thread.setProgress((int) temp);
} this.torqueRefTextView_thread.setText(String.valueOf(temp));
);
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;
}
}
}
I am building the app, which generates and adds view dynamically. I don't know in advance what views I need to create, these can be nested layouts or simple labels, etc, depending what comes back from web services.
Everything has been well so far until I started building really complex nested layouts .I have one case where I need to add about 11 levels of Layouts dynamically. When activity starts I display ProgressDialog(ring), while views are being generated. My problem is that with this complex structure ProgressDialog freezes while views are added. This is the code, which creates the view:
private class ViewCreator implements Runnable {
public BackgroundTaskViewCreatedResponse delegate;
private View mCreatedView;
private ComponentDefinition mComponent;
private ViewCreator(ComponentDefinition component){
this.mComponent = component;
}
#Override
public void run() {
try {
if (mComponent != null){
mComponent.setLinkedData(model.getLinkedData());
mCreatedView = componentCreator.createComponent(mComponent);
}
} finally {
if (mCreatedView != null)
delegate.processFinishTask(mCreatedView);
}
}
}
Layout, which has other views in it implements BackgroundTaskViewCreatedResponse, so, when view is ready, it will be added:
#Override
public void processFinishTask(final View createdView) {
//((Activity)view.getContext()).runOnUiThread(new Runnable(){
mView.post(new Runnable(){
#Override
public void run() {
mView.addView(createdView);
}
});
}
As you can see above, I have tried to call runOnUiThread call, but this blocks the UI thread completely while view hierarchy is being generated. At the same time view.post doesn't get called out of the box, so I have made some changes to views as suggested in this SO answer. So, now my views are added, but my ProgressDialog is not running smoothly. It stops in a few occasions and then resumes. I've also tried using Android AsyncTask, but that gives the same effect as runOnUiThread
I am not very experienced with Threads, have been trying to fix this for a few days now. Please help.
You can use AsyncTask to do this/ Here is an example:
private class GenerateViews extends AsyncTask<Void,Void,Void>{
#Override
protected void onPreExecute() {
// SHOW THE SPINNER WHILE GENERATING VIEWS
spinner.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params) {
//CALL YOUR VIEW GENERATING METHOD HERE
return null;
}
#Override
protected void onPostExecute(Void result){
spinner.setVisibility(View.INVISIBLE);
}
}
You can make this class inside your class, if you want to. And then, you just call
new GenerateCalls.execute();
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();
}