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.
Related
I'm trying to cancle my AsyncTask when I click Back Button by using this code :
#Override
public void onBackPressed() {
super.onBackPressed();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
if(playerTask!=null && !playerTask.isCancelled()) playerTask.cancel(true);
this.finish();
}
But doesn't work playerTask still works :
PlayerTask playerTask = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_for_ringtone);
mediaPlayer = new MediaPlayer();
playerTask = new PlayerTask();
playerTask.execute(url);
/***/
#SuppressLint("StaticFieldLeak")
public class PlayerTask extends AsyncTask<String, Void, Boolean> {
ProgressBar pb = findViewById(R.id.progress);
#Override
protected void onPreExecute() {
super.onPreExecute();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
AudioAttributes attribs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();
mediaPlayer.setAudioAttributes(attribs);
} else {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
}
#Override
protected Boolean doInBackground(String... strings) {
if(!isCancelled()) {
try {
mediaPlayer.setDataSource(strings[0]);
mediaPlayer.prepare();
prepared = true;
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
}
else
{
playerTask.cancel(true);
}
return prepared;
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
music.setEnabled(true);
music.setVisibility(View.VISIBLE);
music.setChecked(true);
}
}
Try this
#Override
public void onBackPressed() {
// if playerTask == null mediaPlayer is never start, no need to handle
if(playerTask != null && playerTask.getStatus() == AsyncTask.Status.FINISHED) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
} else if (playerTask != null && playerTask.getStatus() != AsyncTask.Status.FINISHED) {
// It mean your task is running, should stop your mediaPlayer inside your task
playerTask.cancel(true);
}
super.onBackPressed();
}
And in your PlayerTask override onCancelled method
#Override
protected void onCancelled(Boolean aBoolean) {
if(isCancelled() && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
Hope it help
You have 2 problem to change .
1.When you call super.onBackPressed(); before you canceled the playerTask ,your activity will destroy firstly .So you should call after playerTask canceled .
2.When you judged that the playTask is running , you also need to use playerTask.getStatus() == AsyncTask.Status.RUNNING .
Change
#Override
public void onBackPressed() {
super.onBackPressed();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
if(playerTask!=null && !playerTask.isCancelled()) playerTask.cancel(true);
this.finish();
}
to
#Override
public void onBackPressed() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
if(playerTask!=null && !playerTask.isCancelled() && playerTask.getStatus() == AsyncTask.Status.RUNNING){
playerTask.cancel(true);
playerTask = null;
}
super.onBackPressed();
this.finish();
}
Referring to the docs for AsyncTask.cancel(boolean), AsyncTask cancel(boolean), we see that this method only sets the isCancelled() to give true and results in onCancelled() function to execute instead of onPostExecute(). So if you need to cancel the AsyncTask properly you need to add a flag to periodically check if(!isCancelled()) in doInBackground() and stop execution. Your code would look something like this
#Override
protected Boolean doInBackground(String... strings) {
if(!isCancelled()){
//ToDo:whatever you need to do in the asynctask
}
else{
//ToDo: cancel the execution
}
}
}
MediaPlayer mediaPlayer = new MediaPlayer();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
AudioAttributes attribs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();
mediaPlayer.setAudioAttributes(attribs);
} else {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
music.setEnabled(true);
music.setVisibility(View.VISIBLE);
music.setChecked(true);
}
});
And then onBackPressed() you can do mediaPlayer.release()
I have created the wave while media play with following code,
and stop the player while clicking on the back button or stop button.
Copy in xml file.
<VisualizerView
android:id="#+id/waveform_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"/>
MainActivity.java
private MediaPlayer myMediaPlayer;
private VisualizerView mWaveformView;
Visualizer audioOutput = null;
onCreate()
{
mWaveformView = (VisualizerView) findViewById(R.id.waveform_view);
myButtonPlayLastRecordAudio.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonPlay:
myAudioSavePathInDevice = Environment.getExternalStorageDirectory().toString() + "/" +
"AudioRecording.3gp";
myMediaPlayer = new MediaPlayer();
myMediaPlayer.setVolume(1.5f, 1.5f);
if (myAudioSavePathInDevice == null || myAudioSavePathInDevice.isEmpty()) {
Toast.makeText(MainActivity.this, "No Audio Found", Toast.LENGTH_LONG).show();
} else {
try {
FileInputStream is = new FileInputStream(myAudioSavePathInDevice);
myMediaPlayer.setDataSource(is.getFD());
myMediaPlayer.prepare();
myMediaPlayer.start();
Toast.makeText(AudioRecordingActivity.this, "Recording Playing", Toast.LENGTH_LONG).show();
mWaveformView.clear();
createVisualizer();
} catch (IOException e) {
e.printStackTrace();
}
myMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
audioOutput.setEnabled(false);
}
});
}
break;
case R.id.buttonStopAudio:
mediaPlayerReleaseResources();
break;
}
}
private void createVisualizer() {
int rate = Visualizer.getMaxCaptureRate();
audioOutput = new Visualizer(myMediaPlayer.getAudioSessionId()); // get output audio stream
audioOutput.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
int max = 0, min = 255;
for (int i = 0; i < waveform.length; i++) {
int w = (int) waveform[i] & 0xFF;
max = Math.max(w, max);
min = Math.min(w, min);
}
mWaveformView.addAmplitude((max - min)); // update the VisualizeView
mWaveformView.invalidate(); // refresh the VisualizerView
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
}
}, rate, true, false); // waveform not freq data
audioOutput.setEnabled(true);
}
private void mediaPlayerReleaseResources() {
if(myMediaPlayer != null) {
if (myMediaPlayer.isPlaying()) {
myMediaPlayer.stop();
}
}
if( audioOutput != null)
audioOutput.setEnabled(false);
}
#Override
public void onBackPressed() {
super.onBackPressed();
mediaPlayerReleaseResources();
}
VisualizerView.class
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
public class VisualizerView extends View {
private static final int LINE_WIDTH = 3; // width of visualizer lines
private static final int LINE_SCALE = 4; // scales visualizer lines
private List<Float> amplitudes; // amplitudes for line lengths
private int width; // width of this View
private int height; // height of this View
private Paint linePaint; // specifies line drawing characteristics
private float temp_scale = 3;
float heighest = temp_scale;
// constructor
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs); // call superclass constructor
linePaint = new Paint(); // create Paint for lines
linePaint.setColor(Color.BLACK); // set color to green
linePaint.setStrokeWidth(LINE_WIDTH); // set stroke width
}
// called when the dimensions of the View change
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width = w; // new width of this View
height = h; // new height of this View
amplitudes = new ArrayList<Float>(width / LINE_WIDTH);
}
// clear all amplitudes to prepare for a new visualization
public void clear() {
heighest = 3;
amplitudes.clear();
}
// add the given amplitude to the amplitudes ArrayList
public void addAmplitude(float amplitude) {
amplitudes.add(amplitude); // add newest to the amplitudes ArrayList
// if the power lines completely fill the VisualizerView
if (amplitudes.size() * LINE_WIDTH >= width) {
amplitudes.remove(0); // remove oldest power value
}
}
// draw the visualizer with scaled lines representing the amplitudes
#Override
public void onDraw(Canvas canvas) {
int middle = height / 2; // get the middle of the View
float curX = 0; // start curX at zero
// for each item in the amplitudes ArrayList
for (float power : amplitudes) {
heighest = Math.max(power, heighest);
temp_scale = heighest / height;
float scaledHeight = power / temp_scale; // LINE_SCALE; // scale the power
curX += (LINE_WIDTH); // increase X by LINE_WIDTH
// draw a line representing this item in the amplitudes ArrayList
canvas.drawLine(curX, middle + scaledHeight / 2, curX, middle
- scaledHeight / 2, linePaint);
}
}
}
I've been trying to work this out but to no avail.
I have a score that is obtained in the GameView class using the int hitCount inside the onTouchEvent method. This is being calculated fine. What I need to do is pass the total score to MainMenu class where it says 'Total Number of Hits'.
I have this 'finish' method in the GameActivity class:
public void finish(){
Intent returnIntent=new Intent();
returnIntent.putExtra("GAME_SCORE",gameView.getHitCount());
setResult(RESULT_OK,returnIntent);
super.finish();
}
GameView class:
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
/* Member (state) fields */
private GameLoopThread gameLoopThread;
private Paint paint; //Reference a paint object
/**
* The drawable to use as the background of the animation canvas
*/
private Bitmap mBackgroundImage;
// For creating the game Sprite
private Sprite sprite;
// For recording the number of hits
private int hitCount;
// To track if a game is over
private boolean gameOver;
// To play sound
private SoundPlayer sound;
//int backButtonCount = 0;
public GameView(Context context) {
super(context);
// Focus must be on GameView so that events can be handled.
this.setFocusable(true);
// For intercepting events on the surface.
this.getHolder().addCallback(this);
// Background image added
mBackgroundImage = BitmapFactory.decodeResource(this.getResources(), R.drawable.half_moon);
//sound = new SoundPlayer(this);
}
/* Called immediately after the surface created */
public void surfaceCreated(SurfaceHolder holder) {
// We can now safely setup the game start the game loop.
ResetGame();//Set up a new game up - could be called by a 'play again option'
mBackgroundImage = Bitmap.createScaledBitmap(mBackgroundImage, getWidth(), getHeight(), true);
gameLoopThread = new GameLoopThread(this.getHolder(), this);
gameLoopThread.running = true;
gameLoopThread.start();
}
// For the countdown timer
private long startTime; // Timer to count down from
private final long interval = 1 * 1000; // 1 sec interval
private CountDownTimer countDownTimer; // Reference to the class
private boolean timerRunning = false;
private String displayTime; // To display the time on the screen
//To initialise/reset game
private void ResetGame() {
/* Set paint details */
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(20);
sprite = new Sprite(this);
hitCount = 0;
// Set timer
startTime = 10; // Start at 10s to count down
// Create new object - convert startTime to milliseconds
countDownTimer = new MyCountDownTimer(startTime * 1000, interval);
countDownTimer.start(); // Start the time running
timerRunning = true;
gameOver = false;
}
// Countdown Timer - private class
private class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long startTime, long interval) {
super(startTime, interval);
}
public void onFinish() {
//displayTime = "Time is up!";
timerRunning = false;
countDownTimer.cancel();
gameOver = true;
}
public void onTick(long millisUntilFinished) {
displayTime = " " + millisUntilFinished / 1000;
}
}
//This class updates and manages the assets prior to drawing - called from the Thread
public void update() {
sprite.update();
}
/**
* To draw the game to the screen
* This is called from Thread, so synchronisation can be done
*/
public void doDraw(Canvas canvas) {
//Draw all the objects on the canvas
canvas.drawBitmap(mBackgroundImage, 0, 0, null);
if (!gameOver) {
sprite.draw(canvas);
canvas.drawText("Time Remaining: " + displayTime, 35, 50, paint);
canvas.drawText("Number of hits: " + hitCount, 250, 50, paint);
} else {
canvas.drawText("Game Over!", 185, 100, paint);
canvas.drawText("To go back to the main menu, press the 'back' key", 15, 150, paint);
}
}
//To be used if we need to find where screen was touched
public boolean onTouchEvent(MotionEvent event) {
if (sprite.wasItTouched(event.getX(), event.getY())) {
// This just renews the sprite for now
sprite = new Sprite(this);
//sound.playZapSound();
hitCount++;
}
return true;
}
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.running = false;
// Shut down the game loop thread cleanly.
boolean retry = true;
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
public void getHitCount() {
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
}
MainMenu class:
public class MainMenu extends Activity {
private static final int SCORE_REQUEST_CODE = 1;// The request code for the intent
TextView tvScore;
String score;
Intent gameIntent;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_start);
}
public void startGame(View v) {
gameIntent = new Intent(this, GameActivity.class);
startActivityForResult(gameIntent, SCORE_REQUEST_CODE);
}
/* Create Options Menu */
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, menu);
return true;
}
// Respond to item selected on OPTIONS MENU
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
//put data in Intent
case R.id.easy:
Toast.makeText(this, "Easy chosen", Toast.LENGTH_SHORT).show();
return true;
case R.id.medium:
Toast.makeText(this, "Medium chosen", Toast.LENGTH_SHORT).show();
return true;
case R.id.hard:
Toast.makeText(this, "Hard chosen", Toast.LENGTH_SHORT).show();
return true;
case R.id.other:
Toast.makeText(this, "Other chosen", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent retIntent) {
// Check which request we're responding to
if (requestCode == SCORE_REQUEST_CODE) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
if (retIntent.hasExtra("GAME_SCORE")) {
int scoreFromGame = retIntent.getExtras().getInt("GAME_SCORE");
tvScore.setText(Integer.toString(scoreFromGame));
}
}
}
}
}
Any advice greatly appreciated.
Thanks
UPDATE:
I have added 'return hitCount;' to the 'getHitCount' method in the 'GameView' class and this now just crashes the app when I hit the back key. One the 'MainMenu' class, I have taken out the last line in the 'onActivityResult' method (tvScore.setText(Integer.toString(scoreFromGame)); and it no longer crashes but obviously doesn't pass the score across. I don't know where to go from here.
While fetching data from intent you have to use -
int scoreFromGame = retIntent.getIntExtra("GAME_SCORE", -1);
// -1 is a default value, use accordingly.
Instead of trying to get value from extras.
retIntent.getExtras().getInt("GAME_SCORE");
Use extras only when you put some value inside bundle.
I made a simple class that handles everything related to sound. Has an add, play, stop, release and releaseAll. How it works is that you have to add a song and then call play passing the name of the song you added. Anytime you need to stop, just call the stop function and pass the song's name as parameter and it should stop. My issue is that it isn't stopping even though it goes through stop().
Sound class:
public class Sound
{
private Map<String, MediaPlayer> songs = new HashMap<String, MediaPlayer>();
private MediaPlayer currentlyPlayingSong;
public Sound() {}
public void Add(int songId, String songName, Context context)
{
MediaPlayer song = MediaPlayer.create(context, songId);
songs.put(songName, song);
}
public void Play(String name, boolean shouldLoop)
{
MediaPlayer songToPlay = songs.get(name);
if ( songToPlay != currentlyPlayingSong && songToPlay != null)
{
currentlyPlayingSong = songToPlay;
currentlyPlayingSong.start();
currentlyPlayingSong.setLooping(shouldLoop);
}
}
public void Stop(String name)
{
MediaPlayer songToStop = songs.get(name);
if (songToStop != null)
{
songToStop.setLooping(false);
songToStop.stop();
}
}
public void Release(String name)
{
songs.get(name).release();
}
public void ReleaseAll()
{
LinkedList<MediaPlayer> _songs;
_songs = (LinkedList)songs.values();
for (int i = 0; i < _songs.size(); i++)
{
_songs.get(i).release();
}
}
}
On the activity's OnCreate I call Add then Play. Everything is fine until I try to call Stop from a fragment. Runs without any errors or exceptions, it simply doesn't stop.
Activity:
public class Main extends ActionBarActivity
{
private Sound sound = new Sound();
private static boolean isSoundOn = true;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
isSoundOn = true;
sound.Add(R.raw.drajamainmenueddited, "mainMenuSong", this);
//endregion
//Hide upper action bar
getSupportActionBar().hide();
if (isSoundOn)
sound.Play("mainMenuSong", true);
}
public void SetIsSoundOn(Boolean isOn)
{
isSoundOn = isOn;
}
public boolean GetIsSoundOn()
{
return isSoundOn;
}
public Sound GetSoundObj()
{
return sound;
}
}
Fragment:
public class MainMenuFragment extends Fragment {
private ImageButton soundImgBtn;
private FragmentConfig fragmentConfig;
public MainMenuFragment()
{
fragmentConfig = new FragmentConfig();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//region Initiators
View view = inflater.inflate(R.layout.fragment_main_menu, container, false);
soundImgBtn = (ImageButton)view.findViewById(R.id.soundImgBtn);
//endregion
//region Listeners
soundImgBtn.setOnClickListener(
new View.OnClickListener()
{
#Override
public void onClick(View v)
{
SoundImgBtnClick(v);
}
}
);
//endregion
//Changes audio img
if (((Main)getActivity()).GetIsSoundOn())
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode_off);
else
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode);
// Inflate the layout for this fragment
return view;
}
private void SoundImgBtnClick(View v)
{
//if sound is on and clicked, turn off
if (((Main)getActivity()).GetIsSoundOn())
{
((Main)getActivity()).SetIsSoundOn(false);
((Main)getActivity()).GetSoundObj().Stop("mainMenuSong");
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode);
}
else
{
((Main)getActivity()).SetIsSoundOn(true);
((Main)getActivity()).GetSoundObj().Play("mainMenuSong", true);
soundImgBtn.setImageResource(android.R.drawable.ic_lock_silent_mode_off);
}
}
}
What I'm trying to do is emulate a mute button. Once clicked all sounds should be muted.
This is pretty much all I've coded, so far.
Cheers.
I suspect you're using different instances of MediaPlayer. You are allowed to do that BUT you must stop the song within the same instance.
About the code in Add():
MediaPlayer song = MediaPlayer.create(context, songId);
In Stop():
MediaPlayer songToStop = songs.get(name)
Note:
The above codes tell me you're using different instances of the MediaPlayer for one same song. The object song needs to be declared on a higher scope for you to access it and to stop the song.
Need to call release() method after stop() to free up resources.
try songToStop.release() instead
Got it to stop. My class had to be able to handle one song at a time and many fx at the same time. This is what I came up with.
Sound:
public class Sound
{
private static MediaPlayer currentlyPlayingSong,
currentlyPlayingFX;
public Sound() {}
public void PlayFX(int fxId, Context context, boolean shouldLoop)
{
MediaPlayer fx = MediaPlayer.create(context, fxId);
if (currentlyPlayingFX != fx)
{
StopFX();
currentlyPlayingFX = fx;
currentlyPlayingFX.start();
currentlyPlayingFX.setLooping(shouldLoop);
}
}
public void PlaySong(int songId, boolean shouldLoop, Context context)
{
MediaPlayer song = MediaPlayer.create(context, songId);
if (currentlyPlayingSong != song)
{
StopSong();
currentlyPlayingSong = song;
currentlyPlayingSong.start();
currentlyPlayingSong.setLooping(shouldLoop);
}
}
public void StopFX()
{
if (currentlyPlayingFX != null)
{
currentlyPlayingFX.stop();
currentlyPlayingFX.release();
currentlyPlayingFX = null;
}
}
public void StopSong()
{
if (currentlyPlayingSong != null)
{
currentlyPlayingSong.stop();
currentlyPlayingSong.release();
currentlyPlayingSong = null;
}
}
}
This is was based of what #The Original Android answered. Keep it on a single instance.
Thanks for the help.
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.
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!