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();
}
}
Related
I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}
Learning to use the BroadcastReceiver class in Android, I have written a small program to receive the battery charge state and write it to three TextView fields in an activity.
However, I have made the BroadcastReceiver as a separate class to make it more simple and separate from the activity. Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Does anyone know whether it is possible to make a callback method from the BroadcastReceiver to start a function, f.ex. updateTextViews(); in the Activity?
Here is the source code - note there are two java files:
http://pastebin.com/qjCTsSuH
Regards, Niels.
What worked a charm for me is simply declaring the interface objects as static. Bear in mind though that statics can cause as many problems as they solve as statics persist therir values accross instances.
public class MainActivity extends AppCompatActivity implements SocketMessageReceiver.ISocketMessageReceiver {
//Declare the cb interface static in your activity
private static SocketMessageReceiver.ISocketMessageReceiver iSocketMessageReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//Assign this
iSocketMessageReceiver = this;
socketMessageReceiver.registerCallback(iSocketMessageReceiver);
}
#Override
public void sendSocketMessage(String socketMessage) {
lblEchoMessage.setText(socketMessage);
}
}
And in your Receiver ....
public class SocketMessageReceiver extends BroadcastReceiver {
interface ISocketMessageReceiver {
void sendSocketMessage(String socketMessage);
}
//Also declare the interface in your BroadcastReceiver as static
private static ISocketMessageReceiver iSocketMessageReceiver;
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("com.WarwickWestonWright.SocketExample.RECEIVE")) {
iSocketMessageReceiver.sendSocketMessage(intent.getBundleExtra("DATA").getString("DATA"));
}
}
public void registerCallback(ISocketMessageReceiver iSocketMessageReceiver) {
this.iSocketMessageReceiver = iSocketMessageReceiver;
}
}
I have made the BroadcastReceiver as a separate class to make it more simple
IMHO, you made it more complex.
Therefore I have to find a method to tell my Activity class that the battery data has been updated, or, which is my solution, to pass in references to the TextView fields from the Activity to the BroadcastReceiver class.
Option #1: Just go back to using an inner class for the BroadcastReceiver. ACTION_BATTERY_CHANGED can only be used via registerReceiver() anyway. Just have onReceive() call some method on the activity to do the work of updating the UI.
Option #2: Pass your activity into the constructor of the BroadcastReceiver, and call the method as in option #1.
Option #3: Use an event bus, like Square's Otto or greenrobot's EventBus.
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.
Im currently trying to develop a little game where i want to use a class that extends SurfaceView and have it to draw in i thread (similar to LunarLander). However when i would like to change contentview to the one from xml, the one im not drawing in i was trying to call a method from my surfaceview class that is in activity class that will change contentview by setContentView, i get an RuntimeException:
"Can't create handler inside thread that has not called Looper.prepare()"
It's probably just because im somehow new to android and java development but i don't understand why it will work when the method is a static method but not otherwise?
(method in my Start class that extends activity)
public void simulationDone()
{
.....
}
(trying to access it)
new Start().simulationDone();
A couple of problems:
First, you shouldn't call setContentView multiple times.
Second, your surface view will need a reference to your activity. I tend to define listeners on my views that need to communicate back with an activity. In a custom SurfaceView called MySurfaceView:
public static interface Listener {
public void simulationDone();
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
Then have your activity implement MySurfaceView.Listener and call mySurfaceView.setListener(this) when you create your surface view.
I have a custom gallery view in which I am overriding some methods. I would like to be able to call a function in my main activity from this class. How do I make a reference back to my main class?
I thought I'd just push the class reference into CustomGallery by creating a setter function ---> g.setBaseClass(this);
CustomGallery g = (CustomGallery) findViewById(R.id.playSelectionGallery);
g.setSpacing(10);
g.setCallbackDuringFling(false);
g.setAdapter(new ImageAdapter(this));
g.setSelection(1);
registerForContextMenu(g);
g.setBaseClass(this);
Problem is this is of type Context and someFunctionToCall() will result in a not a member of this class error. In my custom class I have:
public void setBaseClass(Context baseClass)
{
_baseClass = baseClass;
}
private void callSomeFuntionOnMyMainActivityClass()
{
_baseClass.someFunctionToCall();
}
All I want to do is call back to my main class, called ViewFlipperDemo. This would be easy in As3. Any thoughts? Hopefully I'm missing something really simple.
That's actually not a good idea... but you can do it this way:
private void callSomeFuntionOnMyMainActivityClass()
{
((ViewFlipperDemo)_baseClass).someFunctionToCall();
}
What you should do instead is implementing a simple observer which allows you to notify the Activity that something happened. That's one of the main OO principles, your custom class shouldn't know anything about your activity class.
Observer pattern example
The Observer interface:
// TheObserver.java
public interface TheObserver{
void callback();
}
Your custom view:
public class CustomGallery{
private TheObserver mObserver;
// the rest of your class
// this is to set the observer
public void setObserver(TheObserver observer){
mObserver = observer;
}
// here be the magic
private void callSomeFuntionOnMyMainActivityClass(){
if( mObserver != null ){
mObserver.callback();
}
}
// actually, callSomeFuntionOnMyMainActivityClass
// is not a good name... but it will work for the example
}
This is the activity that will benefit of the observer (notice that now you can use your custom view on different activities not just one, that's one of the key reasons to implement it this way):
public class YourActivity extends Activity{
// your normal stuff bla blah
public void someMethod(){
CustomGallery g=(CustomGallery)findViewById(R.id.playSelectionGallery);
g.setObserver(new TheObserver(){
public void callback(){
// here you call something inside your activity, for instance
methodOnYourActivity();
}
});
}
}
You will notice that this design pattern (observer) is widely used in Java and Android... almost any kind of UI event is implemented using observers (OnClickListener, OnKeyListener, etc.). By the way, I didn't test the code, but it should work.