Ok, so I can't quite seem to get this to work, I'm trying to find the proper way to add a delay to a surfaceview runnable. I originally started by using just Thread.sleep, but with so many entries on google saying that using a Thread.sleep for a runnable is bad, I've been trying to "properly" use a handler to achieve essentially the same goal. Here is my code so far that I've tested, notice the handler postdelayed notes and placement
public class demosf extends Activity {
OurView v;
int Measuredwidth;
int Measuredheight;
WindowManager w;
Bitmap whatever;
LinearLayout llMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Measuredwidth = 0;
Measuredheight = 0;
whatever = BitmapFactory
.decodeResource(getResources(), R.raw.dragarrow);
llMain = new LinearLayout(this);
setContentView(llMain);
v = new OurView(demosf.this);
llMain.post(new Runnable() {
#Override
public void run() {
Measuredwidth = llMain.getWidth();
Measuredheight = llMain.getHeight();
llMain.addView(v);
}
});
}
//the Runnable:
public class OurView extends SurfaceView implements Runnable {
Thread t = null;
SurfaceHolder holder;
boolean isItOK;
Handler handler = new Handler();
public OurView(Context context) {
super(context);
holder = getHolder();
}
#Override
public void run() {
//handler.postDelayed placed here freezes app
while (isItOK) {
if (!holder.getSurface().isValid()) {
continue;
}
Canvas c = holder.lockCanvas();
c.drawARGB(255, 0, 0, 0);
c.drawBitmap(
whatever,
((float) this.getWidth() - (float) whatever.getWidth()),
((float) this.getHeight() - (float) whatever
.getHeight()), null);
holder.unlockCanvasAndPost(c);
//handler.postDelayed placed here also freezes app
}
//this one isn't reached
handler.postDelayed(this, 100);
}
public void pause() {
isItOK = false;
while (true) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t = null;
}
public void resume() {
isItOK = true;
t = new Thread(this);
t.start();
}
}
#Override
protected void onPause() {
super.onPause();
v.pause();
}
#Override
protected void onResume() {
super.onResume();
v.resume();
}
}
so do I just stick with using a Thread.sleep? Or how would I use a Handler here? I'm a newb so any help would be very appreciated =)
You are creating the Handler when instantiating OurView, which means the handler will use the current thread, in this case the UI thread - which is not what you want.
See Looper for how to properly create a Looper on a background Thread, which can then be used to create a background Handler.
There is a helper class called HandlerThread in the framework to do this boilerplate for you.
Related
I'm trying to add an Onclicklistener to my SurfaceView. I've put two png images in a loop that looks like a bird flapping its wings. I want a sound to be played when the user clicks on the bird but currently, nothing is happening. I'm not really sure how I would implement soundpool yet but for now, I've just added a toast message just to see if the onClick works. Unfortunately, nothing happens when I click on the bird in the emulator.
Any help on how to implement an on-click listener and soundpool to my surfaceView is much appreciated!
public class PlayActivity extends AppCompatActivity {
surfaceView view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new surfaceView(this);
setContentView(view);
}
public class surfaceView extends SurfaceView implements View.OnClickListener {
public surfaceView(Context context) {
super(context);
new Anim().start();
}
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(),"Bird Clicked",Toast.LENGTH_SHORT).show();
}
private class Anim extends Thread {
int counter = 0;
#Override
public void run() {
long last_updated_time = 0;
long delay = 250;
int[] img_ids = {
R.drawable.bird1,
R.drawable.bird2
};
while (true) {
boolean playing = true;
if (playing) {
long current_time = System.currentTimeMillis();
if (current_time > last_updated_time + delay) {
if (counter >= 2) {
counter = 0;
}
draw(img_ids[counter]);
last_updated_time = current_time;
counter++;
}
}
}
}
private void draw (int img_ids) {
SurfaceHolder holder = getHolder();
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),
img_ids);
Bitmap resized = Bitmap.createScaledBitmap(bitmap,(int)(bitmap.getWidth()*0.2), (int)(bitmap.getHeight()*0.2), true);
canvas.drawBitmap(resized, 100, 100, paint);
holder.unlockCanvasAndPost(canvas);
}
}
}
}
}
It looks like you haven't set an OnClickListener anywhere. You can do so with View.setOnClickListener.
How do i set a timer for TextView ? This code keeps crashing my app
final TextView textwel = (TextView) findViewById(R.id.welcometext);
Thread timer = new Thread() {
public void run() {
try {
// this should sleep for 4 seconds
sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
textwel.setText("Welcome");
}
};
start the timer
timer.start();
That won't work.
Since the new thread that you create is not the UI thread, you can't update the UI (setting the text of a text view) on that thread.
A better way to do this would be to use the android.os.Handler class. It has a postDelayed method that will execute a Runnable with a delay. Here is the docs.
Alternatively, you can use this Timer class I wrote, which encapsulates a Handler instance and has a simple interface:
import android.os.Handler;
public class Timer {
private Handler handler;
private boolean paused;
private int interval;
private Runnable task = new Runnable () {
#Override
public void run() {
if (!paused) {
runnable.run ();
Timer.this.handler.postDelayed (this, interval);
}
}
};
private Runnable runnable;
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public void startTimer () {
paused = false;
handler.postDelayed (task, interval);
}
public void stopTimer () {
paused = true;
}
public Timer (Runnable runnable, int interval, boolean started) {
handler = new Handler ();
this.runnable = runnable;
this.interval = interval;
if (started)
startTimer ();
}
}
Gist
You would probably use it like this:
Runnable run = new Runnable() {
public void run() {
textwel.setText("Welcome");
}
};
Timer timer = new Timer(run, 4000, true);
P.S. setting the textview's text to "Welcome" every 4 seconds is pointless.
You can you use CountDownTimer api to update TextView timer which is very easy to use and convenient.
Here is the simple example:
TextView tvTime = (TextView)findViewById(R.id.tv_time);
CountDownTimer countDownTimer = new CountDownTimer((durationInMilis * 60000), 1000) {
#Override
public void onTick(long millisUntilFinished)
{
String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millisUntilFinished),
TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millisUntilFinished)),
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished)));
tvTime.setText(hms);
}
#Override
public void onFinish()
{
// Do your other task on finish
}
}.start();
This code this will update tvTime text every second and run for sepecified amount of time in durationInMilis. Output will look like hh:mm:ss e.g 01:23:57
If you don't want show anything on update then leave onTick() callback blank and update your TextView in onFinish(). Just pass value to parameter durationInMilis for which you want to run timer.
If you have any doubt please feel free to comment.
//use it, its easy and simple
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
textwel.setText("Welcome");
}
}, 4000);
why are you using textview??
Chronometer is a class provided by android that implements a simple timer so my suggestion is use it in place of textview.
here is the official link
try this , it will work 100% and also UI will not stuck
Thread timer = new Thread() {
public void run() {
try {
// this should sleep for 4 seconds
sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// after sleep call a UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
textwel.setText("Welcome");
}
});
}
};
I am trying to play with progress bars. I have this (below) simple activity which runs a progress bar N times one after the other, when I call Progress(N). It is working great but the problem I am facing is, if I press back button. I get into the mainActivity but the progress bars (the threads) are still running in background one after the other. As soon as they finish N loops, the intent is called and whatever I would be doing would be interrupted by this LOOP_OVER activity.
I tried solving this by my own. I tried using variable of Thread class (before I was directly doing it). And tried to interrupt() it at onDestroy() or even just before the intent is called but its not helping. How should I go about it?
public class Loop extends Activity {
private ProgressBar progressBar;
private CircleProgress circleProgress;
private int progressStatus = 0;
private Handler handler = new Handler();
private TextView myView;
private int started = 0, doneLoop=0;
private Thread th;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loop);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
circleProgress = (CircleProgress) findViewById(R.id.circle_progress);
myView = (TextView) findViewById(R.id.instruction);
progressBar.setScaleY(3f);
// Start long running operation in a background thread
Progress(3);
}
#Override
public void onDestroy() {
// Below, everything I am just
th.interrupt();
Loop.this.finish();
Thread.currentThread().interrupt();
super.onDestroy();
}
public void Progress(final int numberOfRuns){
// QueView.setText(Que);
if(numberOfRuns == 0){
th.interrupt();
Intent myIntent = new Intent(Loop.this, LOOP_OVER.class);
startActivity(myIntent);
super.onDestroy();
finish();
}
th = new Thread(new Runnable() {
public void run() {
genNextSet();
while (progressStatus < 100) {
progressStatus += 1;
// Update the progress bar and display the
//current value in the text view
handler.post(new Runnable() {
public void run() {
circleProgress.setProgress(progressStatus);
progressBar.setProgress(progressStatus);
textView.setText(progressStatus+"/"+progressBar.getMax());
}
});
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
myView.setText(Que);
}
});
// Sleep for 200 milliseconds.
//Just to display the progress slowly
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
progressStatus = 0;
Progress(numberOfRuns - 1);
}
});
th.start();
}
private void genNextSet() {
// so some cool here!
}
}
You can think of a class variable that is shared among all threads.
Try to add something like this:
private Boolean LOOP = true;
then
while (progressStatus < 100 && LOOP) {
and
#Override
public void onBackPressed() {
LOOP = false
}
also
if(LOOP == true){
// call intent
}
finish();
Your activity does not get destroyed, if you press the "Back"-key, thus onDestroy() will not be called.I'd override onBackPressed(), if I where you.Alternatively, you could try to put it into the onPause()-method.
You haven't override the back button pressed..try this
#Override
public void onBackPressed() {
th.interrupt();
Loop.this.finish();
Thread.currentThread().interrupt();
super.onBackPressed();
// add finish() if you want to kill current activity
}
I followed this post to set up an Http Async Request: HttpRequest
So, now, I call: new DownloadTask().execute("http://www.google.com/");
to make this request.
How can I manage different calls? For example:
new DownloadTask().execute("http://www.google.com/");
new DownloadTask().execute("http://www.facebook.com/");
new DownloadTask().execute("http://www.twitter.com/");
And have different results?
Pass one more argument to the AsyncTask. Make some constants corresponding to your tasks.
new DownloadTask().execute("http://www.google.com/", DownloadTask.ID_ASYNC1);
new DownloadTask().execute("http://www.facebook.com/", DownloadTask.ID_ASYNC2);
new DownloadTask().execute("http://www.twitter.com/", DownloadTask.ID_ASYNC3);
Inside AsyncTask, use this id to identify which is the request being called.
private class DownloadTask extends AsyncTask<String, Void, String> {
//Variable for storing the req id
private int id;
//Constants corresponding to your tasks
public static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
static static int ID_ASYNC1 = 0;
#Override
protected String doInBackground(String... params) {
id = params[1]);
//your code
}
#Override
protected void onPostExecute(String result) {
if(id == ID_ASYNC1){
//Do your task #1
} else if(id == ID_ASYNC2){
//Do your task #2
}
}
}
You have to use looper for smooth downloading for multiple file it will download them one by one. In this way your application run smoothly huge huge amount of downloads.
How to use Looper
public class MainActivity extends Activity implements DownloadThreadListener,
OnClickListener {
private DownloadThread downloadThread;
private Handler handler;
private ProgressBar progressBar;
private TextView statusText;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create and launch the download thread
downloadThread = new DownloadThread(this);
downloadThread.start();
// Create the Handler. It will implicitly bind to the Looper
// that is internally created for this thread (since it is the UI
// thread)
handler = new Handler();
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
statusText = (TextView) findViewById(R.id.status_text);
Button scheduleButton = (Button) findViewById(R.id.schedule_button);
scheduleButton.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
// request the thread to stop
downloadThread.requestStop();
}
// note! this might be called from another thread
#Override
public void handleDownloadThreadUpdate() {
// we want to modify the progress bar so we need to do it from the UI
// thread
// how can we make sure the code runs in the UI thread? use the handler!
handler.post(new Runnable() {
#Override
public void run() {
int total = downloadThread.getTotalQueued();
int completed = downloadThread.getTotalCompleted();
progressBar.setMax(total);
progressBar.setProgress(0); // need to do it due to a
// ProgressBar bug
progressBar.setProgress(completed);
statusText.setText(String.format("Downloaded %d/%d", completed,
total));
// vibrate for fun
if (completed == total) {
((Vibrator) getSystemService(VIBRATOR_SERVICE))
.vibrate(100);
}
}
});
}
#Override
public void onClick(View source) {
if (source.getId() == R.id.schedule_button) {
int totalTasks = new Random().nextInt(3) + 1;
for (int i = 0; i < totalTasks; ++i) {
downloadThread.enqueueDownload(new DownloadTask());
}
}
}
}
DownloadThread.Class
public final class DownloadThread extends Thread {
private static final String TAG = DownloadThread.class.getSimpleName();
private Handler handler;
private int totalQueued;
private int totalCompleted;
private DownloadThreadListener listener;
public DownloadThread(DownloadThreadListener listener) {
this.listener = listener;
}
#Override
public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();
Log.i(TAG, "DownloadThread entering the loop");
// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();
// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
Log.i(TAG, "DownloadThread exiting gracefully");
} catch (Throwable t) {
Log.e(TAG, "DownloadThread halted due to an error", t);
}
}
// This method is allowed to be called from any thread
public synchronized void requestStop() {
// using the handler, post a Runnable that will quit()
// the Looper attached to our DownloadThread
// obviously, all previously queued tasks will be executed
// before the loop gets the quit Runnable
handler.post(new Runnable() {
#Override
public void run() {
// This is guaranteed to run on the DownloadThread
// so we can use myLooper() to get its looper
Log.i(TAG, "DownloadThread loop quitting by request");
Looper.myLooper().quit();
}
});
}
public synchronized void enqueueDownload(final DownloadTask task) {
// Wrap DownloadTask into another Runnable to track the statistics
handler.post(new Runnable() {
#Override
public void run() {
try {
task.run();
} finally {
// register task completion
synchronized (DownloadThread.this) {
totalCompleted++;
}
// tell the listener something has happened
signalUpdate();
}
}
});
totalQueued++;
// tell the listeners the queue is now longer
signalUpdate();
}
public synchronized int getTotalQueued() {
return totalQueued;
}
public synchronized int getTotalCompleted() {
return totalCompleted;
}
// Please note! This method will normally be called from the download
// thread.
// Thus, it is up for the listener to deal with that (in case it is a UI
// component,
// it has to execute the signal handling code in the UI thread using Handler
// - see
// DownloadQueueActivity for example).
private void signalUpdate() {
if (listener != null) {
listener.handleDownloadThreadUpdate();
}
}
}
DownloadTask.Class
public class DownloadTask implements Runnable {
private static final String TAG = DownloadTask.class.getSimpleName();
private static final Random random = new Random();
private int lengthSec;
public DownloadTask() {
lengthSec = random.nextInt(3) + 1;
}
#Override
public void run() {
try {
Thread.sleep(lengthSec * 1000);
// it's a good idea to always catch Throwable
// in isolated "codelets" like Runnable or Thread
// otherwise the exception might be sunk by some
// agent that actually runs your Runnable - you
// never know what it might be.
} catch (Throwable t) {
Log.e(TAG, "Error in DownloadTask", t);
}
}
}
DownloadThreadListener.class (interface)
public interface DownloadThreadListener {
void handleDownloadThreadUpdate();
}
Using this you can add huge amount of downloads it will add them queue.
Complete tutorial
I should warn you I'm newbie of android developing.
The problem: After my app starts, it has black screen and after few seconds, it starts to draw what I need. I've checked drawing thread already started, and already drew content several times, but screen is still black :(
What I'm missing?
Here is my code:
Activity
public class MainActivity extends Activity {
protected SurfaceView surface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
surface = new GameSurfaceView(this);
setContentView(surface);
}
}
SurfaceView
public class GameSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
protected DrawThread drawThread;
public GameSurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawThread = new DrawThread(getHolder());
drawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
drawThread.interrupt();
}
}
Drawing thread
public class DrawThread extends Thread {
public static final long FPS = 30;
protected SurfaceHolder surfaceHolder;
public DrawThread(SurfaceHolder surfaceHolder) {
super();
this.surfaceHolder = surfaceHolder;
}
#Override
public void run() {
while (!isInterrupted()) {
Canvas canvas = null;
try {
long renderStartedAt = SystemClock.elapsedRealtime();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.CYAN);
// TODO DRAW
}
long duration = (1000 - (SystemClock.elapsedRealtime() - renderStartedAt) * FPS) / FPS;
if (duration > 0) {
sleep(duration);
}
} catch (InterruptedException e) {
interrupt();
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
I found the solution of my problem! unlockCanvasAndPost() does not send invalidate to SurfaceView, so just adding surface.postInvalidate() right after unlockCanvasAndPost() fix delay on startup.
You can use traceview to find where the holdup is.
Check out the profiling with traceview introduction.
Cheers!