I first want to say I used google a lot to find a progress bar that fills itself when time passes. All results I found where either with a thread or with an Asynctask. As being new to Android, I thought it was easier to accomplish with an extra Handler that handles the updates of the progressbar.
However, I did not find anyone doing it my way. Does it maybe violate Android rules? For example, can I use multiple Handlers at the same time?
My code looks like:
public void restarttimebar()
{
stoptimebar();
for(int i=1;i<12;i++)
{
Message msg = timebarhandler.obtainMessage(0,i,0);
timebarhandler.sendMessageDelayed(msg, i*250);
}
};
public void stoptimebar()
{
timebarhandler.removeMessages(0);
Message msg = timebarhandler.obtainMessage(0,0,0);
timebarhandler.sendMessage(msg);
};
Handler timebarhandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
ProgressBar progressbar = (ProgressBar)findViewById(R.id.timebar);
if (msg.arg1 == 0)
{
progressbar.setProgress(0);
}
if (msg.arg1 > 0)
{
progressbar.setProgress(msg.arg1*9);
}
}
};
Now one can call restarttimebar(); to (re)start the progress bar. It will fill over 3 seconds with increments at each 0.25 second. Any remarks greatly appreciated!!
EDIT: Also added a stoptimebar(); to just stop the progressbar. Also, the standard size of a progressbar is 100. So I just make the twelve updates times 9. It is almost 100 ;) The bar does not have to be THAT very precise in my application.
As far as i know, the threads you create and Asynctasks (Non-UI-Threads) should not touch UI-stuff like your progressbar. If you need your task communicate with the progressbar, use Asynctask and make sure that UI related actions are done in onPostExecute(). This method will be also called by the main UI Thread.
I guess my method of updating the progress bar can be seen as correct. So for anyone who googles and has the same kind of question: Use the code in the question!
Related
I'm trying to create a chat bot. I need to simulate an interactive response from app.The response doesn't come from sever it's embedded inside app.
I'm using recycleview to implement chat UI, I need to delay every message for a specific time so user feels that someone is actually talking to him.
private void displayNewMessage(ArrayList<ChatMessage> messages) {
for (int i = 0; i < messages.size(); i++) {
chatMessages.add(messages.get(i));
}
adapter.notifyDataSetChanged();
}
The previous method is used to push an array of messages to user, how can I delay adding every element to adapter. or push them all to adapter and delay displaying them in adapter it self?
I tried to use Handler but it didn't work, it delays all messages and pushes them all together.
It's a logical question more than programming.
At first why it delays all messages and pushes them all together - because the for loop, it will add them to adapter at once.
An alternative solution is to use handler on some where else suppose on user sending message thread.
assume we have the user sendMessageButton we can make some nice logic there.
We will add message and a time for wait, you can make it Random time, e.g:
sendMessageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
chatMessages.add(newMessage);
adapter.notifyDataSetChanged();
}
},random);
}
});
In your for loop after adding data to arrat put delayedpost on recycler and call notify inside the run method, this will simulate delay for each message, instead of delay and then showing all messages at once
You can use Handler.post delay(new Runnable(),3000);
You put your code under their runnable interface run method it will start with after delay seconds.
So, I have an activity with a handler.
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
matches = LiveAPI.getMatches();
listAdapter.notifyDataSetChanged();
}
LivePage.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Here I get some data and update my list with it. It works.
When I click on an item of my list, this functon is called
private void showLiveMatch(int position) {
Intent i = new Intent(this, LiveMatch.class);
i.putExtra("match", matches.get(position));
startActivity(i);
}
My new activity appears, wich also contains another handler:
private final Runnable m_Runnable = new Runnable()
{
public void run()
{
if(LiveAPI.getStatus() == 1){
match = LiveAPI.getMatch(match.getId());
displayCommentaries();
}
LiveMatch.this.mHandler.postDelayed(m_Runnable, 5000);
}
};
Sometimes this works as I want.
But in some cases it seems like in second activity is still called LiveAPI.getMatches() from the first handler rather than LiveAPI.getMatch(match.getId());
Every function displays a console text, and that's how I figure it out what function is called.
Can someone explain me why?
Once you post either m_Runnable (from LivePage or LiveMatch), it does its stuff and then schedules itself to run in 5 seconds. Basically, each time you start one with a click, it creates an infinite loop. Enough clicks and you will have the logic for each of these running constantly. (That is, unless you have some other code that periodically calls mHandler.removeCallbacks(m_Runnable); that you haven't shown us.) Without knowing more about what you're trying to do, it's hard to recommend how to fix this, but you should somehow avoid creating these kind of infinite loops.
Be aware that all handlers you create on the UI thread simply feed Runnable objects into the (single) MessageQueue for the thread. So there's no such thing as something being called from one handler or another.
I have been doing some research on this but I am still VERY confused to say the least.
Can anyone give me a concrete example of when to use Task and when to use Platform.runLater(Runnable);? What exactly is the difference? Is there a golden rule to when to use any of these?
Also correct me if I'm wrong but aren't these two "Objects" a way of creating another thread inside the main thread in a GUI (used for updating the GUI)?
Use Platform.runLater(...) for quick and simple operations and Task for complex and big operations .
Use case for Platform.runLater(...)
Use case for Task: Task Example in Ensemble App
Example: Why Can't we use Platform.runLater(...) for long calculations (Taken from below reference).
Problem: Background thread which just counts from 0 to 1 million and update progress bar in UI.
Code using Platform.runLater(...):
final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
#Override public void run() {
for (int i = 1; i <= 1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
#Override public void run() {
bar.setProgress(counter / 1000000.0);
}
});
}
}).start();
This is a hideous hunk of code, a crime against nature (and
programming in general). First, you’ll lose brain cells just looking
at this double nesting of Runnables. Second, it is going to swamp the
event queue with little Runnables — a million of them in fact.
Clearly, we needed some API to make it easier to write background
workers which then communicate back with the UI.
Code using Task :
Task task = new Task<Void>() {
#Override public Void call() {
static final int max = 1000000;
for (int i = 1; i <= max; i++) {
updateProgress(i, max);
}
return null;
}
};
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
it suffers from none of the flaws exhibited in the previous code
Reference :
Worker Threading in JavaFX 2.0
Platform.runLater: If you need to update a GUI component from a non-GUI thread, you can use that to put your update in a queue and it will be handled by the GUI thread as soon as possible.
Task implements the Worker interface which is used when you need to run a long task outside the GUI thread (to avoid freezing your application) but still need to interact with the GUI at some stage.
If you are familiar with Swing, the former is equivalent to SwingUtilities.invokeLater and the latter to the concept of SwingWorker.
The javadoc of Task gives many examples which should clarify how they can be used. You can also refer to the tutorial on concurrency.
It can now be changed to lambda version
#Override
public void actionPerformed(ActionEvent e) {
Platform.runLater(() -> {
try {
//an event with a button maybe
System.out.println("button is clicked");
} catch (IOException | COSVisitorException ex) {
Exceptions.printStackTrace(ex);
}
});
}
One reason to use an explicite Platform.runLater() could be that you bound a property in the ui to a service (result) property. So if you update the bound service property, you have to do this via runLater():
In UI thread also known as the JavaFX Application thread:
...
listView.itemsProperty().bind(myListService.resultProperty());
...
in Service implementation (background worker):
...
Platform.runLater(() -> result.add("Element " + finalI));
...
I use some menuitems in my application and I have a question about the run method which I override.
private MenuItem menuItemUpdate = new MenuItem("Update", 0, 0) {
public void run() {
// Can I write GUI code here?
}
};
As the comment states, can I write GUI code here? I can`t right? Since I am not on the GUI thread? Should I use the invokeLater-method when I have code that changes the GUI? And what about Dialogs, should they be invoked in invokeLater-methods as well?
And is it necassary to override the run-method of MenuItem if I have made another thread which will be invoked when the user selects the menu item? Could I start that thread in the constructor instead? And leave the run method un-overridden?
You can write UI code there, because the UI thread handles the user's actions, and calls the menu item code. All user interaction is handled by the UI thread.
Yes, you can - because it's on the UI thread, for example:
private MenuItem menuItemUpdate = new MenuItem("Update", 0, 0) {
public void run() {
Show.status("Huzzah!");
}
};
And it seems to be general rule that you don't add any locking around - until you get an IllegalStateException ;-)
I am quite new to Android and Java. Before I was working with C++ where the events where dispatched with messages. Now I would like to create the same user experience for Android platform and I would appreciate any of your suggestions or comments on what is the best way to bind events to user controls.
Here is an example from C++:
ON_MESSAGE(WM_RECORD_START, &CMainFrame::OnRecordStart)//Method OnRecordStarts() executes on WM_RECORD_START_MESSAGE
...
LRESULT CMainFrame::OnRecordStart(WPARAM wParam, LPARAM lParam)
{
m_pNetworkCtrl->SetGeoLocationInfo();
...
}
...
void CMainFrame::RecordStart()
{
PostMessage(WM_RECORD_START);
}
In the case above the method RecordStart() is bound to a Button (it is executed when a Button is pressed) and posts the message WM_RECORD_START. When the message WM_RECORD_START is received, the method OnRecordStart() is executed.
As mentioned before I would like to create a responsive user interface and am not sure if it would be good enough if the method OnRecordStart() is called directly from RecordStart():
void RecordStart()
{
OnRecordStart();
}
I would really appreciate any of your suggestions.
You can emulator the MFC style behavior in Android by using a Handler to post a Runnable into the message queue.
Here is a brief example
class MyClass
{
Handler m_myHandler;
Runnable m_myRunnable;
MyClass()
{
m_myHandler = new Handler();
m_myRunnable = new RUnnable()
{
public void run()
{
// do your stuff here
}
};
}
public void onclickListener(...)
{
// push the runnable into the message queue
m_myHandler.post(m_myRUnnable);
}
}
You have 2 questions here.
How to bind controls. I use
anonymous inner classes everywhere. It is a bit verbose to type in but auto completion makes it a snap.
how to make the UI responsive.
The key is not to do anything time
consuming in the UI thread. If it
takes more than .1s, do the work in
a worker thread and notify the UI to
update when the task is done. The
other thing is to make sure you
don't generate a lot of garbage
since the android GC is pretty primitive right now.
For Buttons I usually extend Button class and override onTouchEvent()
public boolean onTouchEvent (MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
setPressed(true);
}
return super.onTouchEvent(event);
}