I have a class called CoordMediaPlayer that inherits from MediaPlayer. My CoordMediaPlayer must be able to use the MediaPlayer methods so It's why I use inheritance...
The problem is that to instanciate a MediaPlayer it must use a MediaPlayer.create() static method. It's not like if it just calls a constructor and then I can use method of the parent class normally. If I inherit from MediaPlayer I can't access to the create() method from a class that inherit MediaPlayer I guess because it's static method, I can't Override it.
In ideal I would want to have my CoordMediaPlayer, this CoordMediaPlayer would not contains a MediaPlayer object but I would be able to call all MediaPlayer methods directly from a CoordMediaPlayer instance. It would be still better if I don't have the same kind of static create() method in my CoordMediaPlayer, this creation would be done when I instanciate my CoordMediaPlayer.
The only one way I found to deal with this, is to have a MediaPlayer object in my CoordMediaPlayer, then my CoordMediaPlayer inherit from MediaPlayer and override all the methods I need just by calling the method of my MediaPlayer object... but that looks kind of weird to me... is there any other way to deal with this kind of situation, without having a MediaPlayer in my CoordMediaPlayer but call directly my inherited MediaPlayer methods ?
This is how I deal with this, my CoordMediaPlayer class inherit from MediaPlayer and at the same time contains an instance of a MediaPlayer and have to override all methods of MediaPlayer I want to use...
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
context = getApplicationContext();
int fileResId = context.getResources().getIdentifier("s2", "raw", context.getPackageName());
CoordMediaPlayer cmp = new CoordMediaPlayer(context,fileResId);
cmp.start();
}
CoordMediaPlayer
public class CoordMediaPlayer extends MediaPlayer {
private MediaPlayer mediaPlayer;
private Point coordinates;
#Override
public void start() throws IllegalStateException {
mediaPlayer.start();
}
public CoordMediaPlayer(Context context, int resId) {
mediaPlayer = MediaPlayer.create(context,resId);
}
public void setCoordinates(Point coordinates) {
this.coordinates = coordinates;
}
public Point getCoordinates() {
return coordinates;
}
}
MediaPlayer class actually has public costructor so you don't have to use the create(..) method for contruction. Documentation only suggest to use static methods in some cases.
By the way, the approach you illustrated in your question is called "composition" and is legit and usually thought as better approach than iheritance. Check this stackoverflow response: Prefer composition over inheritance?.
However I'd recomend to think more about reasons, why do you think you need to inherit from the MediaPlayer class, in your case. I honestly think you don't need to and you can just use own class encapsulating MediaPlayer instance and providing it's own api to the rest of your application.
Related
I have some methods to play sounds in MainActivity.
They look like this and they work fine.
public void hydrogen(){
final MediaPlayer mp = MediaPlayer.create(this, R.raw.hydrogen);
mp.start();
}
I thought I'd move them into a class, so I can use the methods in other activities.
This is what I have in the class:
import android.media.MediaPlayer;
public class Sounds {
void hydrogen(){
final MediaPlayer mp = MediaPlayer.create(this, R.raw.hydrogen);
mp.start();
}
}
I get red underlines under "this" and R.raw.hydrogen.
The lines under hydrogen go away when I make the method static, but I can't get rid of the lines under "this".
Does anyone know what I've done wrong?
Problem description is:
Cannot resolve method 'create(com.xxmassdeveloper.lrwhack2.Sounds, int)'
Thanks
First parameter of MediaPlayer.create(Context context, int resid) consumes Context.
If you look into Activity class, it extends Context. Therefore within the functions of your Activity class, you can use this, and it refers to the Activity class you are at.
And for your class Sounds, it is just a simple class without relationship with Context. So you need to pass Context into this class, like following:
public class Sound {
// Create a local variable Context so that you can access it in functions of this Class
private Context context;
// Create a constructor with Context
public Sound(Context context) {
this.context = context;
}
void hydrogen() {
final MediaPlayer mp = MediaPlayer.create(context, R.raw.colors);
mp.start();
}
}
And then you can make use of the passed Context for your function.
I am implementing a music application from this tutorial.
There is a BaseAdapter class used to display the track list, and a MusicPlayer class to play the music. The both are variables of my main activity class.
public class MainActivity extends Activity implements MediaPlayerControl{
private MediaPlayer musicSrv;
private BaseAdapter songAdt;
...
The MusicPlayer play the next tracks when the current finish. What is the best way to send a message to the BaseAdapter to change the displaying at each new playing track (like changing the color of the current track)?
EDIT
According to the comments, it seems that the use of an interface good be a good option. Could someone write a detail answer that explains how to do it? Thanks.
Thanks to the comments, I managed to implement a solution with an interface.
This is my Main activity class, that refresh the BaseAdapater each time the song is changed:
public class MainActivity extends Activity implements MusicService.SongChangedListener {
private MediaPlayer musicSrv;
private BaseAdapter songAdt;
...
#Override
public void songChanged(Song currentSong){
songAdt.notifyDataSetChanged(); // refresh view
}
And my MusicService class:
public class MusicService extends Service implements MediaPlayer.OnPreparedListener{
...
private MainActivity activity;
...
public void setActivity(MainActivity act){
//set the activity
activity = act;
}
public interface SongChangedListener {
void songChanged(Song song);
}
...
public void playSong(){
// this function is called each time a new song is played
activity.songChanged(playSong);
...
}
Maintain one variable in your music track model class which indicates whether this song is in playing mode or not.
Check that value in your getView() and do coding according to it.
if(model.is_playing)
//change your code for playing song
else
//rest songs which are not playing
Now whenever you are changing songs manualy or automaticaly, change that is_playing value, unset it from previous track and set it to currently playing track.
BaseAdapter's method, getView() method will provide you with the view and you should change the color of your of current track by setting a variable in your list and reset that color to default when the variable is not set.
if (is this the current playing track) {
// Set the color of the view.
} else {
// Set the color to default.
}
If you have implemented this logic currently, then whenever you change the current track and also the variable in your list that tracks the current playing Media, a simple songAdt.notifyDataSetChanged() will ask the BaseAdapter to be called again and will set the view as per the new data. For More indepth understanding of ListView you can refer this talk. It will help.
Preferably consider training yourself with RecyclerView, its the present. ListView was a dreadful past.
public class Activity implements SongChangedListener {
...
#Override
onCreate() {
....
PlayerManager pManager = new PlayerManager();
}
onResume() {
pManager.setListener(this);
}
onPause() {
pManager.setListener(null);
}
#Override
void songChanged (MediaId idOfSong) {
if (getActivity == null) //If you received a callback after activity was killed.
return;
// Change the current song as not playing in List. (for your adapter)
// Change the idOfSong to currently playing in List (for your adapter).
// change currentSong = idOfSong;
// notify that the data in List has changed (songAdt.notifyDataSetChanged)
}
}
And in Your PlayerManager, you can create the interface, or maybe a seperate class for the interface, doesn't matter how you send the interface instance.
public class PlayerManager {
...
private SongChangedListener mListener;
...
public PlayerManager() {
}
public void setListener(SongChangedListener listener) {
mListener = listener;
}
public interface SongChangedListener {
void songChanged(MediaId idOfSong);
}
...
public void playSong() {
...
if (mListener != null)
mListener.songChanged(idOfNextSong);
...
}
In your answer you are passing an activity into your service, which feels wrong in many ways. If you want to implement communication between activity and service, there are many other ways to do this. Usually I use a Messenger in conjunction with a Handler. I would provide more details but it would be more beneficial if you explore it in documentation and in other answers. It is easy to implement once you understand how Messengers work.
Also, if you are looking for a fullfledged MediaPlayer Application, your implementation will require a lot more boiler code. Also you will have to handle MediaButton clicks(if someone clicked on play/pause on their bluetooth headphones or on their watch). Preferably MediaSessionCompat is a better implementation. You can also refer the following open Source MediaPlayer, which implements all the minimum required functionalities pretty nicely android-UniversalMusicPlayer.
You don't need to implement your own callback interface. Mediaplayer has already an oncompletionlistener when the playing sound is terminated. So you just need to refresh your adapter in the oncompletion method
public class MainActivity extends Activity implements MediaPlayerControl, MediaPlayer.OnCompletionListener{
private MediaPlayer musicSrv;
private BaseAdapter songAdt;
#Override
public void onCreate() {
musicSrv.setOnCompletionListener(this);
}
#Override
public void onCompletion(MediaPlayer musicPlayer) {
songAdt.notifyDataSetChanged();
}
}
So basically, I'm creating a game on a SurfaceView and I have these classes on my main CustomView:
private TitleScreen titleScreen;private GameScreen gameScreen;private PauseScreen pauseScreen;private GameOverScreen gameOverScreen;
Each of these classes have a draw(Canvas canvas) method, and is called when the user goes to another screen. But the thing is, I have a SoundPlayer class that includes all of my sound effects using SoundPool. It will be used on all these classes. Is there actually a way that the SoundPlayer only loads once, then is available throughout these classes? Or do I have to call release() and recall the constructor everytime I switch? Thanks in advance. :D
UPDATE (SOLVED):
So here's what I did. I created an instance of my SoundPlayer class:
public class SoundPlayerInstance { private static SoundPlayer soundPlayerInstance; private SoundPlayerInstance(){} public static void createInstance(Context context){ soundPlayerInstance = new SoundPlayer(context); } public static SoundPlayer getInstance(){ return soundPlayerInstance; }}
On my main view, before I do anything, I call this in my constructor:
SoundPlayerInstance.createInstance();
Then, on any of my classes, I can just call it to play the sound:
SoundPlayerInstance.getInstance().playSound();
I think this will be useful not only for situations like these, but it can also be useful for developers (like me) that want to instantiate a class that is available throughout all other classes. Thanks to system32 for answering my question. :)
Is there actually a way that the SoundPlayer only loads once, then is
available throughout these classes?
It's possible. Make SoundPlayer class singleton.
public class SoundPlayer
{
private static SoundPlayer instance;
private SoundPlayer()
{
// Do some stuff
}
public static SoundPlayer getInstance()
{
if(instance == null)
instance = new SoundPlayer();
return instance;
}
}
To access globally, just call SoundPlayer.getInstance()
I have an Activity which mainly handles the UI, and I do most of the rest from another class (not a service). One of the things I do from that class is playing audio. What I need to do is tell my Activity when the audio finished playing (OnCompletionListener).
public class MyClass implements OnCompletionListener {
private MyActivity activity = new MyActivity();
public MyClass(){
}
...........
...........
...........
#Override
public void onCompletion(MediaPlayer mp) {
activity.onComplete();
}
}
This is wrong because "Cannot make a static reference to the non-static method onComplete() from the type MyActivity.
I'm pretty sure I'm using Java wrong, but I cant figure out how to call onComplete from the class. (changing onComplete to static isn't posible).
EDIT:
added a constructor to MyActivity:
public MyActivity(){
}
and created an instance of MyActivity, activity (see the edited code above), passed it to the method, but when I do activity.onComplete(); it stops unexpectedly. (I dont know why my logcat isnt working, I'll post back when I get it to work)
Just pass an instance of MyActivity to MyClass and call a method on it. It's arguable approach but best I can suggest having information that you provided.
When programming for Android sometimes you have to use static methods. But when you try to access you resources in a static method with getString(R.string.text) you'll get an error. Making it static doesn't work.
Does anyone knows a good way around this? The resource files in Android are very helpful for creating things in different languages or making changes to a text.
One way or another, you'll need a Context for that... For static methods this probably means you need to pass along a Context when calling them.
You could use Resources.getSystem().getStringArray(android.R.array.done);
This is how I access resources from inside static methods. Maybe not ideal, but.
First, I extend Application and set some public static field(s), and create a method to initialise them:
public class MyApp extends Application {
// static resources
public static String APP_NAME;
public static void initResources(Context context) {
APP_NAME = context.getResources().getString(R.string.app_name);
}
}
And in my manifest I register the extended Application:
<application
android:name=".MyApp"/>
In my starter activity (MainActivity), I make a call to initialise the static resources:
#Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.initResources(this);
}
Then anywhere in your project, after MainActivity.onCreate(Bundle b) has run, you can call static methods that access your specified static resources:
public static void printAppName() {
Log.w("tag", "my app name: " + MyApp.APP_NAME);
}
Pass in a Context (i.e. Activity) instance as a parameter object to static method. Then invoke getString on the parameter.
The post below gives a tip for creating an Application class to save your current context. Your new Application class will then be accessible from any other static method.
How can I get a resource content from a static context?
One way is you can pass context to your static method.
check this out it definitely works
public class Sounds {
public static MediaPlayer getSoundTouch(Context context){
return MediaPlayer.create(context, R.raw.touch);
}
public static MediaPlayer getSoundLeak(Context context){
return MediaPlayer.create(context, R.raw.leak);
}
public static MediaPlayer getSoundFinish(Context context){
return MediaPlayer.create(context, R.raw.finish);
}
}