I have a ListView which displays voice messages (just like WhatsApp's), I'm using BaseAdapter, and my layout contains a play button and a SeekBar.
What I want is to play the audio on the click of the play button, and update the SeekBar.
So I implemented this custom SeekBar which updates itself.
SelfUpdatingSeekBar.java:
public class SelfUpdatingSeekBar extends SeekBar {
MediaPlayer mp;
boolean mActive;
public void setMediaPlayer(MediaPlayer mp){
this.mp = mp;
}
Runnable mUpdate = new Runnable() {
#Override
public void run() {
long totalDuration = mp.getDuration();
long currentDuration = mp.getCurrentPosition();
// Updating progress bar
int progress = (int)(getProgressPercentage(currentDuration, totalDuration));
setProgress(progress);
postDelayed(this, 100);
}
} ;
public void setActive(int progress) {
if (!mActive) {
mActive = true;
removeCallbacks(mUpdate);
setProgress(progress);
post(mUpdate);
}
}
public void setInactive(int progress) {
if (mActive) {
mActive = false;
removeCallbacks(mUpdate);
}
setProgress(progress);
}
public SelfUpdatingSeekBar(Context context) {
super(context);
}
public SelfUpdatingSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelfUpdatingSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SelfUpdatingSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public int getProgressPercentage(long currentDuration, long totalDuration){
Double percentage = (double) 0;
long currentSeconds = (int) (currentDuration / 1000);
long totalSeconds = (int) (totalDuration / 1000);
// calculating percentage
percentage =(((double)currentSeconds)/totalSeconds)*100;
// return percentage
return percentage.intValue();
}
}
This is how my getView() looks like:
public View getView(final int i, View convertView, ViewGroup viewGroup){
final ViewHolder viewHolder;
if(convertView == null){
LayoutInflater inflater = ((MainActivity) context).getLayoutInflater();
viewHolder = new ViewHolder();
convertView = inflater.inflate(R.layout.sent_voice_bubble, viewGroup, false);
viewHolder.sent_voice_seekbar = (SelfUpdatingSeekBar) convertView.findViewById(R.id.sent_seekbar);
viewHolder.sent_voice_seekbar.setMediaPlayer(mMediaPlayer);
viewHolder.sent_voice_play = (ImageView) convertView.findViewById(R.id.sent_playAudio);
viewHolder.sent_voice_container = convertView.findViewById(R.id.sent_vmessagesection);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(((int)(x * 0.7f)), RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
viewHolder.sent_voice_container.setLayoutParams(params);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.sent_voice_play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Check if the Item is currently playing
if(list.get(i).isPlaying()){
// Stop audio and save progress
stopItemAudio(list.get(i), viewHolder.sent_voice_seekbar);
// Change button's image to Pause ||
((ImageView) v).setImageDrawable(((MainActivity) context).pauseDrawable);
}else{
// Start audio and update the SeekBar
playItemAudio(list.get(i), viewHolder.sent_voice_seekbar);
// Change button's image to Play >
((ImageView) v).setImageDrawable(((MainActivity) context).playDrawable);
}
}
});
if(list.get(i).isPlaying()){
viewHolder.sent_voice_seekbar.setActive(list.get(i).seekbar_resume_position);
viewHolder.sent_voice_play.setImageDrawable(((MainActivity) context).pauseDrawable);
}else{
viewHolder.sent_voice_seekbar.setInactive(list.get(i).seekbar_resume_position);
viewHolder.sent_voice_play.setImageDrawable(((MainActivity) context).playDrawable);
}
return convertView;
}
public void stopItemAudio(AudioRow item, SelfUpdatingSeekBar seekBar){
if(mMediaPlayer == null){
mMediaPlayer = new MediaPlayer();
}
mMediaPlayer.stop();
item.setPlaying(false);
int percentage = getProgressPercentage(mMediaPlayer.getCurrentPosition(), mMediaPlayer.getDuration());
item.setSeekBarResumePosition(percentage);
item.setMediaPlayerResumePosition(mMediaPlayer.getCurrentPosition());
seekBar.setInactive(percentage);
notifyDataSetChanged();
}
public void playItemAudio(final AudioRow item, SelfUpdatingSeekBar seekBar){
if(mMediaPlayer == null){
mMediaPlayer = new MediaPlayer();
}
try {
mMediaPlayer.reset();
mMediaPlayer.setDataSource(item.audioPath);
mMediaPlayer.prepare();
mMediaPlayer.seekTo(item.mediaPlayer_resume_position);
seekBar.setActive(item.seekbar_resume_position); // Starts from where it stopped
mMediaPlayer.start();
// declaring Item started to play
item.setPlaying(true);
notifyDataSetChanged();
mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
item.setSeekBarResumePosition(0);
item.setPlaying(false);
}
});
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public int getProgressPercentage(long currentDuration, long totalDuration){
Double percentage = (double) 0;
long currentSeconds = (int) (currentDuration / 1000);
long totalSeconds = (int) (totalDuration / 1000);
// calculating percentage
percentage =(((double)currentSeconds)/totalSeconds)*100;
// return percentage
return percentage.intValue();
}
}
The problem is when I click on Item1, and it starts playing, and click on Item2 both SeekBars animate at the same time, and that is expected since I did not stop Item1 on the click of Item2
My question is:
What is the proper way to stop the previous played Item1 on the click of Item2?
Your SeekBar implementation should not have its own MediaPlayer. Have only one MediaPlayer instance in your activity. Every time a "play" button is clicked in your list view you do something like this:
MediaPlayer mMediaPlayer = ((MainActivity) context).getSingleMediaPlayer();
((MainActivity) context).setCurrentlyPlayingItem(i);
mMediaPlayer.reset();
mMediaPlayer.setDataSource(item.audioPath);
mMediaPlayer.prepare();
notifyDatasetHasChanged()
Now on your getView() implementation, if the first argument: i is not equal to the currently playing item of MainActivity its SeekBar should be at zero.
MainActivity should also be the one implementing the update runnable. At this point you might want to use just a normal SeekBar. To update the correct View, you need to figure out first if it is visible, because the user could have scrolled to a different position, so you need this function in your MainActivity:
public View getVisibleItemView(int position){
if(listview.getCount() > 0) {
int start = listview.getFirstVisiblePosition();
for (int i = start, j = listview.getLastVisiblePosition(); i <= j; i++) {
if (i == position) {
return listview.getChildAt(i - start);
}
}
}
return null;
}
This function returns the view with the SeekBar that you want to update, but only if it is visible, otherwise it returns null. in your update runnable you should call it, and if the result is not null, you update the SeekBar.
Also, you might want to consider using an interface instead of casting context references to MainActivity in your adapter.
Related
Hi Community,
I'm developing whats-app like application.In that I am facing a
problem in recycle view while scrolling the items.The problem is even
specific to audio files itself.These files were playing irrelevant to
item data position.Please help me to resolve this issue.
MyAdapterClass:
#Override
public void onBindViewHolder(SwappingHolder holder, int position) {
MessageDetails messageData = messagesList.get(position);
if (messageData != null) {
switch (messageData.getMessageType()) {
case MessageDetails.MESSAGE_TEXT_TYPE:
TextTypeViewHolder textViewHolder = (TextTypeViewHolder) holder;
textViewHolder.updateView(messageData);
break;
case MessageDetails.MESSAGE_IMAGE_TYPE:
ImageTypeViewHolder imageViewHolder = (ImageTypeViewHolder) holder;
imageViewHolder.updateImageFile(messageData);
break;
case MessageDetails.MESSAGE_AUDIO_TYPE:
AudioTypeViewHolder audioHolder = (AudioTypeViewHolder) holder;
audioHolder.initializeAudioPlayer(messageData);
break;
case MessageDetails.MESSAGE_VIDEO_TYPE:
VideoTypeViewHolder videoHolder = (VideoTypeViewHolder) holder;
videoHolder.updateVideoMessage(messageData);
break;
case MessageDetails.MESSAGE_LOCATION_TYPE:
MapTypeHolder mapHolder = (MapTypeHolder) holder;
mapHolder.loadMapThumbView(messageData);
break;
case MessageDetails.MESSAGE_CONTACT_TYPE:
ContactsTypeHolder contactsHolder = (ContactsTypeHolder) holder;
contactsHolder.setContactData(messageData);
break;
case MessageDetails.MESSAGE_FILE_TYPE:
FileTypeHolder fileTypeHolder = (FileTypeHolder)holder;
fileTypeHolder.updateFileData(messageData);
}
}
}
MyHolderClass:
public class AudioTypeViewHolder extends SwappingHolder implements View.OnClickListener, View.OnLongClickListener {
SeekBar seekBar;
TextView timer, tv_time;
ImageView btn_play, iv_smile;
CardView cardView;
RelativeLayout parentView;
CustomizedAudioPlayer player;
MessageDetails messageDetails;
MediaPlayer mediaPlayer;
public AudioTypeViewHolder(View itemView) {
super(itemView, multiSelector);
this.timer = (TextView) itemView.findViewById(R.id.timer);
this.seekBar = (SeekBar) itemView.findViewById(R.id.audio_seekBar);
this.btn_play = (ImageView) itemView.findViewById(R.id.btnPlay);
this.tv_time = (TextView) itemView.findViewById(R.id.timeright);
this.cardView = (CardView) itemView.findViewById(R.id.card_view);
this.parentView = (RelativeLayout) itemView.findViewById(R.id.parent_view);
this.iv_smile =(ImageView) itemView.findViewById(R.id.smile1);
this.mediaPlayer = new MediaPlayer();
btn_play.setOnClickListener(this);
player = new CustomizedAudioPlayer(mediaPlayer, seekBar, btn_play, timer);
itemView.setOnLongClickListener(this);
itemView.setLongClickable(true);
btn_play.setOnLongClickListener(this);
btn_play.setLongClickable(true);
}
void initializeAudioPlayer(MessageDetails messageDetails) {
int colorId, gravity;
this.messageDetails = messageDetails;
if(messageDetails.getAudioPlayer() == null)
messageDetails.setAudioPlayer(player);
if (messageDetails.getSender().equals("to")) {
colorId = mContext.getResources().getColor(R.color.color_recevied_item);
gravity = Gravity.LEFT;
iv_smile.setVisibility(View.GONE);
} else {
colorId = mContext.getResources().getColor(R.color.color_sent_item);
gravity = Gravity.RIGHT;
iv_smile.setVisibility(View.VISIBLE);
iv_smile.setBackgroundResource(updateMessageStatus(messageDetails.getMstatus()));
}
player.initializePlayer(messageDetails.getMessage());
tv_time.setText(messageDetails.getTime());
cardView.setCardBackgroundColor(colorId);
parentView.setGravity(gravity);
}
#Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.btnPlay) {
if (!mediaPlayer.isPlaying()) {
// check my player is playing
//if not, stop if any other player is playing
for (MessageDetails messageDetails : messagesList) {
CustomizedAudioPlayer customPlayer = messageDetails.getAudioPlayer();
if(customPlayer != null)
customPlayer.stopTheMediaPlay();
}
//start my player
btn_play.setImageResource(R.drawable.img_pause);
player.startThePlayer();
notifyDataSetChanged();
} else {
btn_play.setImageResource(R.drawable.img_play);
player.stopTheMediaPlay();
}
}
}
#Override
public boolean onLongClick(View view) {
onItemLongPressed(this, messageDetails);
return true;
}
}
My Custom Audio Player
public class CustomizedAudioPlayer implements SeekBar.OnSeekBarChangeListener,MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener {
private Handler myHandler = new Handler();
SeekBar seekbar;
MediaPlayer mediaPlayer;
boolean isPaused;
ImageView playBtn;
TextView timer;
StringBuilder mFormatBuilder;
Formatter mFormatter;
int totalDuration, currentDuration;
String path, chatId;
public CustomizedAudioPlayer(MediaPlayer mediaPlayer, SeekBar seekbar, ImageView view, TextView timer){
this.seekbar = seekbar;
this.playBtn = view;
this.timer = timer;
this.mediaPlayer = mediaPlayer;
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
this.seekbar.setProgress(0);
this.seekbar.setMax(100);
this.seekbar.setOnSeekBarChangeListener(this);
}
public void initializePlayer(String filePath){
try{
if(path == null){
this.path = filePath;
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
timer.setText(stringForTime( mediaPlayer.getDuration()));
}
}catch (Exception e){
}
}
public void startThePlayer(){
totalDuration = mediaPlayer.getDuration();
mediaPlayer.seekTo(currentDuration);
myHandler.postDelayed(updateProgress,100);
mediaPlayer.start();
}
public void stopTheMediaPlay(){
if(mediaPlayer.isPlaying()){
playBtn.setImageResource(R.drawable.img_play);
currentDuration = mediaPlayer.getCurrentPosition();
timer.setText(stringForTime( mediaPlayer.getDuration()));
myHandler.removeCallbacks(updateProgress);
mediaPlayer.pause();
}
}
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
playBtn.setImageResource(R.drawable.img_play);
seekbar.setProgress(0);
timer.setText(stringForTime( mediaPlayer.getDuration()));
myHandler.removeCallbacks(updateProgress);
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
myHandler.removeCallbacks(updateProgress);
playBtn.setImageResource(R.drawable.img_play);
mediaPlayer.pause();
isPaused = true;
}
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
currentDuration = progressToTimer(seekBar.getProgress(), totalDuration);
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
if(fromUser){
mediaPlayer.seekTo(currentDuration);
}
} else if (mediaPlayer == null) {
seekBar.setProgress(0);
}
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
currentDuration = progressToTimer(seekBar.getProgress(), totalDuration);
if (mediaPlayer.isPlaying() && mediaPlayer != null || (isPaused && mediaPlayer != null)) {
isPaused = false;
mediaPlayer.seekTo(currentDuration);
myHandler.postDelayed(updateProgress,100);
mediaPlayer.start();
playBtn.setImageResource(R.drawable.img_pause);
}
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
return false;
}
private Runnable updateProgress = new Runnable() {
public void run() {
totalDuration = mediaPlayer.getDuration();
currentDuration = mediaPlayer.getCurrentPosition();
int progress = getProgressPercentage();
float progressPer = ((float)progress/100);
float newPosition = (totalDuration) * progressPer;
seekbar.setProgress(progress);
timer.setText(stringForTime( (int) newPosition));
myHandler.postDelayed(this, 100);
}
};
private String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
public int getProgressPercentage(){
long currentSeconds = (currentDuration / 1000);
long totalSeconds = (totalDuration / 1000);
Double percentage =(((double)currentSeconds)/totalSeconds)*100;
return percentage.intValue();
}
public int progressToTimer(int progress, int totalDuration) {
totalDuration = (totalDuration / 1000);
int currentDuration = (int) ((((double)progress) / 100) * totalDuration);
// return current duration in milliseconds
return currentDuration * 1000;
}
}
I even tried by placing
setIsRecyclable(false);
Using this, the positions of items are stable, but the item not updating while playing audio file(i.e. seekbar, file time not updating)
You can closely find my problem in following screenshot
I have a Main Activity and in the corresponding XML Layout I have a Custom View that draws game objects (my tank and 10 enemies), a few buttons to control my tank and fire bullets and a TextView to show my score. My custom view is a GameSurfaceView java class that is a half screen game board.
Here is some of my code:
public class GameSurfaceView extends SurfaceView implements Runnable {
private static Context gContext;
public GameSurfaceView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
resume();
gContext = context;
}
public void resume() {
isRunning = true;
gameThread = new Thread(this);
gameThread.start();
}
public void pause() {
isRunning = false;
boolean retry = true;
while (retry) {
try {
gameThread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
#Override
public void run() {
while (isRunning) {
// We need to make sure that the surface is ready
if (!holder.getSurface().isValid()) {
continue;
}
long started = System.currentTimeMillis();
// update
step();
// draw
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
render(canvas);
holder.unlockCanvasAndPost(canvas);
}
//detect all possible collisions
detectCollisions();
float deltaTime = (System.currentTimeMillis() - started);
int sleepTime = (int) (FRAME_PERIOD - deltaTime);
if (sleepTime > 0) {
try {
gameThread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0) {
step();
sleepTime += FRAME_PERIOD;
}
}
}
//Called from MainActivity
public void dispatchKey(int tDirection) {
Toast.makeText(gContext, "Hi", Toast.LENGTH_LONG).show();
gameStarted = true;
if (tDirection == FIRE)
Fire();
else if (tDirection != tank.Direction)
turnTankDirection = tDirection;
}
private void detectCollisions() {
//Collision Detection between tank and enemy
Toast.makeText(gContext, "Collision", Toast.LENGTH_LONG).show();
}
}
My questions:
1- Why the Toast in dispatchKey() runs correctly but Toast in detectCollisions() makes a force close?
2- How to update TextView in detectCollisions() method?
3- How to show a DialogAlert when a collision detected in detectCollisions() method?
My problem relates mainly to gContext variable.
Thanks.
Regarding question 1: Maybe this effects the second thread. While dispatchKey() is called from Activity, detectCollision() is invoked from the sureface-thread. Did you tried to call detectCollision() from activity?
Regarding question 3: let your activity implement a listener, which will be called if collision is detected. The same thing you could use as solution of question 1 and 2.
You should try:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
//Make toast or manipulate TextView
}
});
everybody.
First of all, I'm a newbie. I created an activity to play a specific mp3 from raw folder. It works fine and playing it well. I implemented the Media Player as you will see in my code below. But the problem is when I change the orientation of my smartphone , the player continues playing the sound, but a new activity is opened/ updated with the same elements reseted and if I click on PLAY button, the mp3 starts playing, but the old activity stills running on back, playing the mp3.... so, there are 2 same songs playing now...
I don't want to update the activity or open a new one. I just want to continue playing the song where it was playing before I change the smartphone orientation.
Here is my code:
MP3PlayerActivity.java (main)
public class Mp3PlayerActivity extends Activity implements OnPreparedListener {
/** Called when the activity is first created. */
private Button btnPlay;
private Button btnPause;
private int current = 0;
private boolean running = true;
private int duration = 0;
private MediaPlayer mPlayer;
private SeekBar mSeekBarPlayer;
private TextView mMediaTime;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.goodthings);
btnPlay = (Button) findViewById(R.id.button1);
btnPause = (Button) findViewById(R.id.button2);
mMediaTime = (TextView)findViewById(R.id.mediaTime);
mSeekBarPlayer = (SeekBar)findViewById(R.id.progress_bar);
mPlayer.setOnPreparedListener(this);
mSeekBarPlayer.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(fromUser){
mPlayer.seekTo(progress);
updateTime();
}
}
});
btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
mPlayer.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mPlayer.start();
mSeekBarPlayer.postDelayed(onEverySecond, 1000);
}
public void onCompletion( MediaPlayer mPlayer)
{
mPlayer.release();
}
});
btnPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
mPlayer.pause();
}
public void onCompletion( MediaPlayer mPlayer)
{
mPlayer.release();
}
});
}
private Runnable onEverySecond = new Runnable() {
#Override
public void run(){
if(true == running){
if(mSeekBarPlayer != null) {
mSeekBarPlayer.setProgress(mPlayer.getCurrentPosition());
}
if(mPlayer.isPlaying()) {
mSeekBarPlayer.postDelayed(onEverySecond, 1000);
updateTime();
}
}
}
};
private void updateTime(){
do {
current = mPlayer.getCurrentPosition();
System.out.println("duration - " + duration + " current- "
+ current);
int dSeconds = (int) (duration / 1000) % 60 ;
int dMinutes = (int) ((duration / (1000*60)) % 60);
int dHours = (int) ((duration / (1000*60*60)) % 24);
int cSeconds = (int) (current / 1000) % 60 ;
int cMinutes = (int) ((current / (1000*60)) % 60);
int cHours = (int) ((current / (1000*60*60)) % 24);
if(dHours == 0){
mMediaTime.setText(String.format("%02d:%02d / %02d:%02d", cMinutes, cSeconds, dMinutes, dSeconds));
}else{
mMediaTime.setText(String.format("%02d:%02d:%02d / %02d:%02d:%02d", cHours, cMinutes, cSeconds, dHours, dMinutes, dSeconds));
}
try{
Log.d("Value: ", String.valueOf((int) (current * 100 / duration)));
if(mSeekBarPlayer.getProgress() >= 100){
break;
}
}catch (Exception e) {}
}while (mSeekBarPlayer.getProgress() <= 100);
}
#Override
public void onPrepared(MediaPlayer arg0) {
// TODO Auto-generated method stub
duration = mPlayer.getDuration();
mSeekBarPlayer.setMax(duration);
mSeekBarPlayer.postDelayed(onEverySecond, 1000);
}
}
when you rotate your device, onCreate() is called again, so all the stuff you do in onCreate() is done all over again - which means recreating your MediaPlayer and re-setting all of the variables.
What you need to do is only create those things if this is the first time onCreate() is being called.
Have a look here for more info on that.:
http://developer.android.com/guide/topics/resources/runtime-changes.html
I gonna show a preview using a PopupWindow based on AirView feature of Samsung SPen
But the problem is that the SurfaceView is not created and non of the SurfaceHolder.Callback methods are called.
The surface region becomes transparent when the popup is displayed because the surface is not created at all.
SurfaceView is not created and is transparent:
HoverPreview:
public class HoverPreview extends LinearLayout implements View.OnHoverListener, SurfaceHolder.Callback {
private static final String TAG = "HoverPreview";
private SurfaceHolder mHolder = null;
View mAnchorView = null;
String videoPath;
int position;
private boolean IsMediaPlayerReady = false;
private MediaPlayer mMediaPlayer;
private SurfaceView mSurfaceView;
Context context;
public HoverPreview(Context context, String videoPath, int position) {
super(context);
this.videoPath = videoPath;
this.position = position;
setupLayout(context);
}
public HoverPreview(Context context, AttributeSet attrs) {
super(context, attrs);
setupLayout(context);
}
public HoverPreview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setupLayout(context);
}
private void setupLayout(Context context) {
this.context = context;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.media_browser_hover, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "HoverSurface created");
final Surface surface = surfaceHolder.getSurface();
if (surface == null) return;
if (!surface.isValid()) return;
mHolder = surfaceHolder;
mMediaPlayer = new MediaPlayer();
try {
mMediaPlayer.setDataSource(videoPath);
} catch (IOException e) {
e.printStackTrace();
}
mMediaPlayer.setDisplay(mHolder);
mAnchorView.setTag(mMediaPlayer);
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
#Override
public void onVideoSizeChanged(MediaPlayer mediaPlayer, int i, int i2) {
mHolder.setFixedSize(i, i2);
}
});
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG, "MediaPlayer preview is prepared");
IsMediaPlayerReady = true;
if (mMediaPlayer != null && IsMediaPlayerReady) {
if (position > 0)
mMediaPlayer.seekTo(position);
mMediaPlayer.start();
}
}
});
Log.d(TAG, "MediaPlayer is created");
try {
mMediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
Log.d(TAG, "HoverSurface changed");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "HoverSurface destroyed");
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
//thumbnailImageView.setTag(null);
}
}
public void setAnchorView(View view) {
mAnchorView = view;
}
#Override
public boolean onHover(View view, MotionEvent motionEvent) {
try {
if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
Log.d(TAG, "ACTION_HOVER_ENTER");
mSurfaceView = (SurfaceView) findViewById(R.id.media_browser_hoverSurfaceView);
mHolder = mSurfaceView.getHolder();
if (mHolder != null) {
mHolder.addCallback(this);
}
} else if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
Log.d(TAG, "ACTION_HOVER_EXIT");
if (mAnchorView.getTag() != null) {
MediaPlayer mMediaPlayer = (MediaPlayer) mAnchorView.getTag();
mMediaPlayer.stop();
mMediaPlayer.release();
mAnchorView.setTag(null);
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage() + Utils.toString(e.getStackTrace()));
}
return false;
}
}
The code to show the preview:
final PopupWindow popupWindow = new PopupWindow(context);
final HoverPreview hoverPreview = new HoverPreview(context, videoPath, 0);
hoverPreview.setAnchorView(thumbnailImageView);
thumbnailImageView.setOnHoverListener(new View.OnHoverListener() {
#Override
public boolean onHover(View view, MotionEvent motionEvent) {
hoverPreview.onHover(view, motionEvent);
if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
popupWindow.setContentView(hoverPreview);
popupWindow.setWidth(600);
popupWindow.setHeight(400);
popupWindow.showAtLocation(thumbnailImageView, ToolHoverPopup.Gravity.NO_GRAVITY, 10, 10);
Log.d(TAG, "Manual Hover Enter");
} else if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
Log.d(TAG, "Manual Hover Exit");
if (popupWindow != null)
popupWindow.dismiss();
}
return true;
});
Here's my complete working solution:
I borrowed some code from ToolHoverPopup class from SPen library, also I customized for this special popup so that nothing is created or inflated until the actual hovering is happened so that we don't consume resources for enabling such a preview in lists.
We need to have our preview attached to a Window so because of this we have to manage all the underlying job of positioning which is normally done by PopupWindow, so I completely removed the dependency on the PopupWindow and now my HoverPreview class is fully working and manages all the jobs, also it has the ability to determine the Hover Detection delay in milliseconds.
Screenshot (SurfaceView is created)
Usage: (Since the layout contains SurfaceView and is resource intensive, I manually trigger onHover event so that the real surface creation is performed only when the real hover is performed. Also by this, I don't create any object of HoverPreview before it's needed)
thumbnailImageView.setOnHoverListener(new View.OnHoverListener() {
#Override
public boolean onHover(View view, MotionEvent motionEvent) {
HoverPreview hoverPreview;
if (thumbnailImageView.getTag() == null) {
hoverPreview = new HoverPreview(context, getActivity().getWindow(), videoPath, 0);
hoverPreview.setHoverDetectTime(1000);
thumbnailImageView.setTag(hoverPreview);
} else
hoverPreview = (HoverPreview) thumbnailImageView.getTag();
hoverPreview.onHover(null, motionEvent);
if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_EXIT)
thumbnailImageView.setTag(null);
return true;
}
});
HoverPreview:
public class HoverPreview extends LinearLayout implements View.OnHoverListener, SurfaceHolder.Callback {
private static final int MSG_SHOW_POPUP = 1;
private static final int MSG_DISMISS_POPUP = 2;
private static final int HOVER_DETECT_TIME_MS = 300;
private static final int POPUP_TIMEOUT_MS = 60 * 1000;
protected int mHoverDetectTimeMS;
private static final String TAG = "HoverPreview";
private SurfaceHolder mHolder = null;
String videoPath;
int position;
private boolean IsMediaPlayerReady = false;
private MediaPlayer mMediaPlayer;
private SurfaceView mSurfaceView;
Context context;
private HoverPopupHandler mHandler;
Window window;
public HoverPreview(Context context, Window window, String videoPath, int position) {
super(context);
this.mHoverDetectTimeMS = HOVER_DETECT_TIME_MS;
this.videoPath = videoPath;
this.position = position;
this.window = window;
setupLayout(context);
}
private void setupLayout(Context context) {
this.context = context;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rootView = inflater.inflate(R.layout.media_browser_hover, this);
mSurfaceView = (SurfaceView) findViewById(R.id.media_browser_hoverSurfaceView);
}
View rootView;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Log.d(TAG, "HoverSurface created");
final Surface surface = surfaceHolder.getSurface();
if (surface == null) return;
if (!surface.isValid()) return;
mHolder = surfaceHolder;
mMediaPlayer = new MediaPlayer();
try {
mMediaPlayer.setDataSource(videoPath);
} catch (IOException e) {
e.printStackTrace();
return;
}
mMediaPlayer.setDisplay(mHolder);
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG, "MediaPlayer preview is prepared");
IsMediaPlayerReady = true;
int videoWidth = mMediaPlayer.getVideoWidth();
int videoHeight = mMediaPlayer.getVideoHeight();
Point size = new Point();
int screenHeight = 0;
int screenWidth = 0;
Display display = getDisplay();
display.getSize(size);
screenWidth = size.x - (350 + 30); // margin + padding
screenHeight = size.y;
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mSurfaceView.getLayoutParams();
lp.width = screenWidth;
lp.height = (int) (((float) videoHeight / (float) videoWidth) * (float) screenWidth);
mSurfaceView.setLayoutParams(lp);
if (mMediaPlayer != null && IsMediaPlayerReady) {
if (position > 0)
mMediaPlayer.seekTo(position);
mMediaPlayer.start();
findViewById(R.id.media_browser_hoverRootFrameLayout).setVisibility(VISIBLE);
}
}
});
Log.d(TAG, "MediaPlayer is created");
try {
mMediaPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
Log.d(TAG, "HoverSurface changed");
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
Log.d(TAG, "HoverSurface destroyed");
try {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
} catch (Exception e) {
}
}
#Override
public boolean onHover(View view, MotionEvent motionEvent) {
try {
if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
Log.d(TAG, "ACTION_HOVER_ENTER");
show(); // checks the timing
} else if (motionEvent.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
Log.d(TAG, "ACTION_HOVER_EXIT");
dismiss();
}
} catch (Exception e) {
Log.e(TAG, e.getMessage() + Utils.toString(e.getStackTrace()));
}
return false;
}
/**
* Sets the time that detecting hovering.
*
* #param ms The time, milliseconds
*/
public void setHoverDetectTime(int ms) {
mHoverDetectTimeMS = ms;
}
public void dismiss() {
dismissPopup();
}
private void dismissPopup() {
// remove pending message and dismiss popup
getMyHandler().removeMessages(MSG_SHOW_POPUP);
getMyHandler().removeMessages(MSG_DISMISS_POPUP);
try {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
} catch (Exception e) {
}
if (getParent() != null)
((ViewGroup) getParent()).removeView(this);
}
private Handler getMyHandler() {
if (mHandler == null)
mHandler = new HoverPopupHandler();
return mHandler;
}
public void show() {
// send message to show.
if (getMyHandler().hasMessages(MSG_SHOW_POPUP)) {
return;
// getHandler().removeMessages(MSG_SHOW_POPUP);
}
getMyHandler().sendEmptyMessageDelayed(MSG_SHOW_POPUP, mHoverDetectTimeMS);
}
private void showPopup() {
if (getParent() == null) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER_VERTICAL;
params.x = 350;
window.addContentView(this, params);
}
mHolder = mSurfaceView.getHolder();
if (mHolder != null) {
mHolder.addCallback(this);
}
}
;
private class HoverPopupHandler extends Handler {
#Override
public void handleMessage(Message msg) {
// if (DEBUG)
// android.util.Log.e(TAG, "handleMessage : " + ((msg.what == MSG_SHOW_POPUP) ? "SHOW" : "DISMISS"));
switch (msg.what) {
case MSG_SHOW_POPUP:
showPopup();
sendEmptyMessageDelayed(MSG_DISMISS_POPUP, POPUP_TIMEOUT_MS);
break;
case MSG_DISMISS_POPUP:
dismissPopup();
break;
}
}
}
}
The first question that I have is that I have downloaded from the Internet MarkableImageView class to add a marker to an image I have already selected. The class is as follows:
public class MarkableImageView extends ImageView {
// Por defecto falso
private boolean checked = false;
public MarkableImageView(Context context) {
super(context);
}
public MarkableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MarkableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setChecked(boolean checked) {
this.checked = checked;
invalidate();
}
public boolean isChecked() {
return checked;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(checked) {
Bitmap check = BitmapFactory.decodeResource(
getResources(), R.drawable.checkbox_on_background);
int width = check.getWidth();
int height = check.getHeight();
//int margin = 15;
int marginWidth = (int)(canvas.getWidth()/15);
int marginHeight = (int)(canvas.getHeight()/20);
int x = canvas.getWidth() - width - marginWidth;
//int y = canvas.getHeight() - height - margin;
int y = marginHeight;
canvas.drawBitmap(check, canvas.getWidth()-check.getWidth(), 1, new Paint());
}
}
}
I use a Gridview to show all my photos and I want to marked some. I use a BaseAdapter to load the data, here is my method getView ():
public View getView(int position, View convertView, ViewGroup parent) {
MarkableImageView imageView;
if(convertView == null) {
imageView = new MarkableImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams((int)(Preferencias.anchoPantalla/3.5), (int)(Preferencias.altoPantalla/4)));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
imageView = (MarkableImageView) convertView;
}
if(nivel == -1) {
if(!Preferencias.getIdsImagenesSeleccionadasJuegoMemoria().isEmpty()) {
if(Preferencias.getIdsImagenesSeleccionadasJuegoMemoria().size() > position) {
imageView.setImageResource(
Preferencias.imagenesTodas[Preferencias.getIdsImagenesSeleccionadasJuegoMemoria().get(position)]);
}
}
} else if(nivel == 0) {
if(!listaIdsDescubiertas.isEmpty()) {
if(listaIdsDescubiertas.size() > position) {
imageView.setImageResource(Preferencias.imagenesTodasPequeñas[listaIdsDescubiertas.get(position)]);
}
}
} else {
switch(nivel) {
case 1:
imageView.setImageResource(Preferencias.imagenesNivel1[position]);
break;
case 2:
imageView.setImageResource(Preferencias.imagenesNivel2[position]);
break;
}
if(!listaIdsDescubiertas.isEmpty()) {
for(Integer pos : listaIdsDescubiertas) {
if(calcularPosicion(position, nivel) == pos) {
imageView.setChecked(true);
}
}
}
}
return imageView;
}
Everything works perfectly but the problem is that when I start to move around the screen looks brand in the photos that I want to stop playing but the screen in certain positions marks are removed. Then touch the screen again reappears. How I can fix it to appear forever?
My second question is that I generate a grid where each image is a class called TileJuegoMemoria extending ImageView.
for (int r = 0; r < rows; r++) {
LinearLayout row = new LinearLayout(this);
row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
row.setGravity(Gravity.CENTER);
for (int c = 0; c < columns; c++) {
TileJuegoMemoria tile = new TileJuegoMemoria(this, tilebackResource,
tilesImagenesRepetidas.get(position), position, sideLength, sideLength);
tile.setClickable(true);
tile.setId(position);
tile.setOnClickListener(tileOnClickListener);
row.addView(tile);
position++;
}
memoryBoardLayout.addView(row);
}
I add each object to a listener to click on it. The Listener has its method onClick () like this:
tileOnClickListener = new OnClickListener() {
#Override
public void onClick(View v) {
TileJuegoMemoria tempTile = (TileJuegoMemoria) v;
if (tempTile.getVisibility() == TileJuegoMemoria.INVISIBLE || (!tempTile.isBackVisible)) {
return;
}
int move = gameEngine.doMove(tempTile);
if (move == -1) {
return;
}
gameEngine.selectedTile = tempTile;
gameEngine.selectedTile.flipOver();
if (move == 2) {
final Handler handler = new Handler();
Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
TileJuegoMemoria precedentMoveselectedTile = gameEngine.movesArray[0];
if (gameEngine.selectedTile.tileFace.equals(precedentMoveselectedTile.tileFace)) {
if(eliminarAcertados) {
gameEngine.selectedTile.hide();
precedentMoveselectedTile.hide();
}
} else {
gameEngine.selectedTile.flipOver();
precedentMoveselectedTile.flipOver();
turnoJugador1 = !turnoJugador1;
}
gameEngine.clearMoveArray();
}
});
}
}, 1000);
}
}
};
}
The game is for 2 players where they take turns playing two screen images that change. My question is how it might do so after Player 1's turn, was the computer who did click on two random images.
Where gender had thought the images saved in a list the coordinates of each image to PerformClick () on it. What I can do? Thanks!