clipProgress resets a progress bar to zero:
public class Whatever extends Activity implements ListenerA, ListenerB {
private Handler mHandler = new Handler();
ProgressBar myProgressBar;
int myProgress = 0;
public void clipProgress() {
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
myProgressBar.setProgress(myProgress);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
clipProgress();
public void onClick(View view) {
Duration.duration = 0;
startRecording();
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(50);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
});
}
I have two different methods, below, where I suppose I could attempt to reset the progress bar to zero. But they both reside in a different class entirely:
#Override protected void onPostExecute(Void result) {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
}
and
public void stopThis(){
thing.stop();
try {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Eclipse wants to apply "static" to various initializations in the Whatever class. But those fixes just leave findViewById(R.id.progressbar) redlined.
I just want to reset the progress bar (which was started from within class 1) from class 2. It's too much to ask for.
UPDATE:
Whatever is the starting point, the Android Activity. Its handler, which I should have included in the above snip, right underneath onCreate is included in the snip below. So more completely, and invoked from a button, is thus:
public void onClick(View view) {
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(87);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
Once that button is pressed a listener is produced:
myTask = new Task();
myTask.addTaskListener(this);
myTask.execute(this);
It is inside the Task class:
public class Task extends AsyncTask<Whatever, ArrayList<Float>, Void>{
...
public void addTaskListener(TaskListener rl){
this.rl = rl;
}
...
that those two functions reside, one of which I'm hoping to use to stop the progress bar.
A task is designed elsewhere to shut down at exactly a constant number of seconds, and so I can hardcode 50 into that progress bar value on the sleep(). I just want to be able to shut the bar down back to zero earlier if I stop the task earlier.
Eclipse warns you because you are accessing the clipProgress method in a static way (by calling Whatever.clipProgress()).
Also, you should only assign your progress bar once, so move
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
into your onCreate method.
You should probably pass the ProgressBar into the other class that needs to update it, and then do something like
myProgressBar.post(new Runnable() {
public void run() {
myProgressBar.setProgress(myProgress);
}
});
this post method updates the ProgressBar on the UI thread, which is the only safe way to do it. Make sure you are only doing any of this if the activity with the ProgressBar is currently active.
You should also question whether you need to update the progress bar in this other class at all. I only see you updating the myProgress variable inside your handler, where you set the progress on the bar immediately after. Could you explain a bit more about the relationship between this other class and the activity with your progress bar?
This has more to do with Java programming and instance / static methods than with Android:
When you call Whatever.clipProgress() you're attempting to make a call to a static method call to clipProgress on the Whatever class, which is why Eclipse wants to add the static modifier.
However, findViewById() is an instance method (read the link above) on Activity, which is presumably some ancestor of your Whatever class. Static methods can't call instance methods statically, so even if you let Eclipse make clipProgress static, you need to call clipProgress on an instance of your activity.
Your other class needs to have a reference to the instance of the class on which you want to call clipProgress(). You should either pass in that reference to the other class, or as another poster noted, pass in a reference to the ProgressBar itself.
Then you can call e.g. mWhatever.clipProgress() or progressBar.setProgress(0).
Related
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. :)
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
I have a simple logic on my app that looks for a certain pitch.
The problem is that the logic is in the OnCreate method of the app (it has to detect the pitch the moment the application is running).
It is a little bit ugly as I plan to add some more logic as the application starts.
Does anyone have any advice of how to move that code to a different class so that it could be invoked from there?
The class still has to access views in the main activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}
Basically, you have two options to make your code cleaner.
Move all the code in onCreate() (except first two lines) into another method, let's say lookForPitch(). Then you can call it right in onCreate().
If you plan to create more methods that focus on audio processing, you can create separate class, for example AudioUtils.java. This util class should contain public static methods, that you can invoke from any place in your code. In case of onCreate() you may call it like this: AudioUtils.lookForPitch(). Also if you want to handle Views, that are accessible only in your Activity, you can pass them as argument. So your method in AudioUtils can look like this:
public static void lookForPitch(TextView myTextView) {
// your code goes here
}
Just make it a method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myLongAndSweetMethod();
}
private void myLongAndSweetMethod(){
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}
Then using the Code folding of Android Studio to hide it.
If you want to improve the readability of your code I can recommend the book "Clean Code: A Handbook of Agile Software Craftmanship" by Robert C. Martin (aka Uncle Bob).
This book is really great! It helped me to a lot to make my code more clean and easy to read. It is a book you should have read if you want to be(come) a professional software developer.
I have an activity or form in which there is one text box called time here. As suggested by experts from this forum I am using runnable to update the TextBox while receiving the data from wifi.
My doubt is what to do when I want to update multiple TextBox's. Should I use multiple blocks of runnables like
time1.post(new Runnable() {
#Override
public void run() {
time2.setText(s1);
}
});
time2.post(new Runnable() {
#Override
public void run() {
time2.setText(s2);
}
});
time3.post(new Runnable() {
#Override
public void run() {
time3.setText(s2);
}
});
Or some other technique is there to update multiple TextBoxes? My present code is like below.
package com.example.cdttiming;
public class MainActivity extends Activity
{
EditText time;
String s;
Button button;
byte[] buffer = new byte[65535];
InetAddress ia = null;
byte[] bmessage = new byte[1500];
DatagramPacket dp = new DatagramPacket(bmessage, bmessage.length);
MulticastSocket ms = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
time = (EditText) findViewById(R.id.et_time);
try
{
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
ia = InetAddress.getByName("226.1.1.1");
try {
ms = new MulticastSocket(4321);
} catch (IOException e) {
e.printStackTrace();
}
try {
ms.joinGroup(ia);
} catch (IOException e) {
e.printStackTrace();
}
ms.setReuseAddress(true);
}
catch (UnknownHostException e) {
time.setText(e.getMessage());
}
catch (IOException e) {
time.setText(e.getMessage());
}
}
public void startProgress(View view) {
Runnable runnable = new Runnable() {
#Override
public void run() {
while(true) {
try {
// String str="This is test string";
ms.receive(dp);
s = new String(dp.getData(),0,dp.getLength());
char retval[] = s.toCharArray();
}
catch (UnknownHostException e) {
time.setText(e.getMessage());
}
catch (IOException e) {
time.setText(e.getMessage());
}
****////// My doubt is here if i have multple strings of data and multiple
/// multiple textboxes to update then what to do ???****
time.post(new Runnable() {
#Override
public void run() {
time.setText(s);
}
});
} // while
}
};
new Thread(runnable).start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Your code is a bit confusing. In one case inside your primary while loop you are capturing the data, assigning it to a String variable s then using the text widget post() function and a Runnable to set the EditText widget to that value. But inside that same while loop you have exception handlers that simply set the same EditText widget directly. Your code also looks like you could potentially lose messages if the while loop resets the value of s before the timer loop has a chance to fire the set text call.
It appears you are trying to create some form of real-time system and need the primary while loop to continually process, and display data as it becomes available. Now you have 3 different consumers (text widgets), but you didn't mention if you also have 3 different sources of messages or is there still only one main processing loop and some form of selector will decide which text widget gets the message?
Were I building something along these lines, I would probably use a messaging system and follow the producer-consumer model. When text was received, I would have the primary processing loop push a simple 2-field message onto a queue that contained a reference to the text widget and a reference to the data string. Because Strings are immutable in Java, once the message object had its own copy of the text, any changes to s would not affect the message.
Then, I would have a second thread running in the background that consumes the message queue. It would pull the message off the queue, construct a post call to the target text widget with the message data, fire it off, then go back to get the next message.
By going this route you separate the data processing thread from the UI update processing and would not need to worry about how many text widgets or other widgets you need updated. If you ever need to add others you only need to worry about the new widgets being known to the code that creates the update messages. The thread doing the widget update doesn't know how many widgets you have, it simply uses the one referenced in the update message object that the message creator said to use.
I suggest you only create one runnable and post it once on main thread like this :
time1.post(new Runnable() {
#Override
public void run() {
time2.setText(s1);
time2.setText(s2);
time3.setText(s3);
}
});
The need to create a runnable and to post it on the main thread handler of a view is only about running a piece of code on the UI thread. No matter where you get the main thread handler reference from.
you could also have created your how handler on main thread :
protected void onCreate(Bundle savedInstanceState)
{
this.uiThrdHandler = new Handler();
}
then post a runnable using it :
this.uiThrdHandler.post(new Runnable(){
...
});
Of course there is no need to create another handler but it's for demonstration purpose.
The Activity object has an utility method for that purpose : runOnUiThread
Using it, it would be :
MainActivity.this.runOnUiThread (new Runnable() {
#Override
public void run() {
time2.setText(s1);
time2.setText(s2);
time3.setText(s3);
}
});
But again, the result is the same.
Intro: I have made a Base Activity to extend my other activities to. I have overriden several methods with runnables in the function bodies, for example:
#Override
protected void onStop(){
new Handler().postDelayed(new Runnable()
{
#Override
public void run()
{
BaseActivity.super.onStop();
}
}, Fade.fadeDuration);
}
However, I get a SuperNotCalledException when I try to run the app. If I take the super.onStop() out of the runnable, I get no exception whatsoever.
Question: How do I call the super.onStop from a runnable in a base activity without causing a SuperNotCalledException?
Additional info: I am trying to add a fadeoutanimation which only fades out certain views. This takes about 700ms so I need to delay the onStop for 700ms as well. The problem is that this is a hassle to code in every activity. I want to make a base activity so I don't have to worry about the fading in every single activity.
If you are trying to simply delay execution of the super.onStop I would use a CountDownLatch.
Maybe something like this :
private void CountDownLatch latch;
private void long latchWait = 10L; // seconds
private void TimeUnit latchWaitUnit = SECONDS;
#Override
protected void onStop(){
try{
this.latch.await(this.latchWait, this.latchWaitUnit);
catch(InterruptedException e){
// Handle
}finally{
super.onStop();
}
}
public void startLatch(long wait){
this.latch = new CountDownLatch(1);
this.latchWait = wait;
}
public void releaseLatch(){
this.latch.countDown()
}
I did not test this code.