I hope this will be simple question.
I have main activity, on this activity I create an instance of some class. How to send some event form one class to main one? How to setup some kind a listener to send notifications between classes. Only option what I know/use right now is to keep reference to parent class and call directly some function from child class.
I'm wonder if it possible to create something like is in ActionScript, where I can call to dispatchEvent(new Event("name")) and later setup addEventlistener("name" function) ??
If "I implement some class" means that you have declared a nested class inside your Activity class than nested non-static class will have a reference to parent class object.
In general, you can always create dispatcher/listener pattern your self. Create listener interface and add either addListener or setListener method to class that will dispatch event.
Example of listener:
public interface IAsyncFetchListener extends EventListener {
void onComplete(String item);
void onError(Throwable error);
}
Example of event dispatcher:
public class FileDownloader {
IAsyncFetchListener fetchListener = null;
...
private void doInBackground(URL url) {
...
if (this.fetchListener != null)
this.fetchListener.onComplete(result);
}
public void setListener(IAsyncFetchListener listener) {
this.fetchListener = listener
}
}
Example of class with event listener:
public class MyClass {
public void doSomething() {
FileDownloader downloader = new FileDownloader();
downloader.setListener(new IAsyncFetchListener() {
public void onComplete(String item) {
// do something with item
}
public void onError(Throwable error) {
// report error
}
});
downloader.start();
}
}
Just implement a listener(or a list of listeners) on the class that generates the events.
When an event is generated iterate over this list and call a method that all the listeners must implement (via an interface maybe?)
Hope it helped,
JQCorreia
Related
I have a use case in which I need to update points(markers) on the map when a certain event occurs inside a service. For that am using an interface callback method.
public interface draw
{
void updatePoints(Context context) throws JSONException;
}
Now am using this interface in my Service like this :
draw dfc;
...............
//Inside a constructor of a class
dfc=new draw() {
#Override
public void updateFloatingPoints(Context context) throws JSONException {
System.out.println("Callback in Service");
CustomViewView.registerDPCListener(dfc);
}
};
Now within the same Service, under a certain function, am passing the interface as an input parameter i.e callback and inside that function am calling
callback.updateFloatingPoints(context);
Now when am running the service, I can see the callback in Service statement. Till here everything is okay.
Now problem is that, when the callback is received, I want to do some work another class. Let's call it CustomView. I implemented the interface in that class too but I am not able to receive the callbacks over there. Follow is the bare bone code of the class
public class CustomView extends ABCD implements draw {
private draw dfcListner=null;
public void registerDPCListener(drawPath listener) throws JSONException {
this.dfcListner = listener;
if(dfcListner!=null)
{
System.out.println("Listener initialized");
//dfcListner.updateFloatingPoints(mcontext);
}
else
{
System.out.println("Listener is not initialized");
}
}
//Another functions in between over here
#Override
public void updateFloatingPoints(Context context) throws JSONException {
System.out.println("Inside CustomView callback");
}
}
How do I go about it?
I'm new to interfaces and I'm trying to do the following. What am I missing?
public class MyAdapter implements ItemManager.DoThisInterface {
...
#Override
doThis() {
// Do things specific to my adapter. Define action hre.
}
}
The interface is defined in the Item Manager, which does not know what needs to be done. The adapter should define the actions.
public class ItemManager {
....
private void onCertainEvent() {
doThis(); // do whatever is overriden in adapter.
// this is kind of a placeholder for what i expect to be defined
// in the adapter.
// (this fails to compile because it can't call straight to the interface method)
}
// interface declaration
public interface DoThisInterface {
doThis();
}
}
You may need to create an object.
MyAdapter ma=new MyAdapter();
ma.doThis();
Call this and your problem will be solved.
You should set a DoThisInterface instance on the class ItemManager, and save it in the instance field, then use the instance to call the interface method, like this
public class ItemManager {
private DoThisInterface mDoThisInterface;
....
private void onCertainEvent() {
if(mDoThisInterface != null){
mDoThisInterface.doThis();
}
}
public void setDoThisInterface(DoThisInterface doThisInterface){
mDoThisInterface = doThisInterface;
}
// interface declaration
public interface DoThisInterface {
doThis();
}
}
Is it possible to make a secondary class to hold the OnClick Listener? Meaning not being created in the Activity class?
I just find that putting OnClick listeners in the main activity class is just messy and I would rather have them in separate classes. Thanks
Sure, that's possible. Just create a class that implements View.OnClickListener and set that as listener to the View. For example:
public class ExternalOnClickListener implements View.OnClickListener {
public ExternalOnClickListener(...) {
// keep references for your onClick logic
}
#Override public void onClick(View v) {
// TODO: add code here
}
}
And then set an instance of above class as listener:
view.setOnClickListener(new ExternalOnClickListener(...));
The parameterized constructor is optional, but it's very likely you'll need to pass something through to actually make your onClick(...) logic work on.
Implementing a class anonymously is generally easier to work with though. Just a thought.
Instead of putting the onCLicklistener in a separate class, why dont you try to define onClickListener outside onCreate()??
For e.g: like this
onCreate()
yourViewName.setOnClicklistener(listener):
Outside onCreate()
private OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
};
Yes you can. However, making the listener an inner class has one advantage - it can access the fields and variables of your activity class directly. If you make it a separate class, and your listener actually need to access 5 views, your listener constructor might look like this:
MyListener listener = new MyListener(context, button, textView1, textView2, ratingBar, imageView);
Which is kinda bulky too. If your listener is simple, go ahead and make it a separate class. Otherwise, its up to you for readability.
Let me share how I code it using MVP. It's the best way to make clean code. Remember each class must have an interface to control it. I will show you the simplest one.
Suppose you want to Toast a text onClick and control it from another class. Here's how it works. Creating interfaces is for nothing but to connect with each other and you can review the code easily.
Create an interface for that MainActivity class.
public interface MainActivityView {
void showToast();
}
Create another interface for the Presenter class.
public interface IMainPresenter<V extends MainActivityView> {
/*Generic Type is to make sure it comes from MainActivity class only and to avoid other class to access it.*/
void onAttach(V mainView);
void onButtonClick();
}
Remember interfaces are nothing but to override method for each class.
Create a Presenter class
public class MainPresenter<V extends MainActivityView> implements IMainPresenter<V> {
private V mainActivityView;
#Override
public void onAttach(V mainActivityView) {
this.mainActivityView=mainActivityView;
}
public V getView() {
return mainActivityView;
}
#Override
public void onButtonClick() {
getView().showToast(); //This is the method from MainActivity controlling with this class
}
}
I'll skip, activity_main.xml layout because there's just a button with id="#+id/buttonId." In MainActivityClass,
public class MainActivity extends AppCompactActivity implements MainActivityView {
Button btn;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainPresenter mainPresenter = new MainPresenter();
mainPresenter.onAttach(this);
btn = findViewById(R.id.buttonId);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mainPresenter.onButtonClick(); //Here, check No.3 again!
}
});
}
#Override
public void showToast() {
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show();
}
}
All I want to tell you is that. If you create objects in a class, it cannot make unit testing. That's why you're not seeing any new objects calling in android. So, you can use a singleton pattern (Here is Lazy Type) in Presenter class. I'll remove its interface and Generic to see it clearly.
public class MainPresenter {
private static final MainPresenter mainPresenter = new MainPresenter();
MainPresenter() {}
public static MainPresenter getInstance() {
return mainPresenter;
}
//Some methods here can be get it once you create an object with getInstance();
}
And so you can get its methods from MainActivity like this.
Instead of creating objects like this...
MainPresenter mainPresenter = new MainPresenter();
You can get it like this...
MainPresenter mainPresenter = mainPresenter.getInstance();
More example for singleton pattern can be found here,
https://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
Finally, using static is not a very good choice because it uses memory space whether you use it or not. And so, you can create objects within Application Layer get it with a Typecasting. I'm sure you don't need to unit test that Application layer.
public class AppLayer extends Application {
private MainPresenter mainPresenter;
#Override
public void onCreate() {
super.onCreate();
mainPresenter = new MainPresenter();
}
public MainPresenter getMainPresenter() {
return mainPresenter;
}
And you need to give a class name within Application in manifest.xml
<application
android:name=".AppLayer"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
</application>
And you can get it with a Typecast in MainActivity like this!
MainPresenter mainPresenter = ((AppLayer)getApplication()).getMainPresenter();
For further studies, I suggest you learn ButterKnife, Dagger 2 and SOLID Principles. It will help you to create clean coding. Have fun!
You can do it. But just think that you will not have a reference to the activity, neither to it's attributes, including all the views. (unless you make them public or accessible with getters methods).
Also, be extra carefull with storing references to the activity or any members on the listener, since they might avoid the garbage collector from getting the listener memory back.
public class CommonClick {
public static void commonClick(final AppCompatActivity context){
context.findViewById(R.id.appbar).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
}
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.
During the most recent Google IO, there was a presentation about implementing restful client applications. Unfortunately, it was only a high level discussion with no source code of the implementation.
In this diagram, on the return path there are various different callbacks to other methods.
How do I declare what these methods are?
I understand the idea of a callback - a piece of code that gets called after a certain event has happened, but I don't know how to implement it. The only way I've implemented callbacks so far have been overriding various methods (onActivityResult for example).
I feel like I have a basic understanding of the design pattern, but I keep on getting tripped up on how to handle the return path.
In many cases, you have an interface and pass along an object that implements it. Dialogs for example have the OnClickListener.
Just as a random example:
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callbackCall() {
// callback code goes here
}
}
worker.callback = new Callback();
// Option 2:
worker.callback = new MyCallback() {
void callbackCall() {
// callback code goes here
}
};
I probably messed up the syntax in option 2. It's early.
When something happens in my view I fire off an event that my activity is listening for:
// DECLARED IN (CUSTOM) VIEW
private OnScoreSavedListener onScoreSavedListener;
public interface OnScoreSavedListener {
public void onScoreSaved();
}
// ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD
// FROM WITHIN ACTIVITY
public void setOnScoreSavedListener(OnScoreSavedListener listener) {
onScoreSavedListener = listener;
}
// DECLARED IN ACTIVITY
MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
slider.setOnScoreSavedListener(new OnScoreSavedListener() {
#Override
public void onScoreSaved() {
Log.v("","EVENT FIRED");
}
});
If you want to know more about communication (callbacks) between fragments see here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
No need to define a new interface when you can use an existing one: android.os.Handler.Callback. Pass an object of type Callback, and invoke callback's handleMessage(Message msg).
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
to clarify a bit on dragon's answer (since it took me a while to figure out what to do with Handler.Callback):
Handler can be used to execute callbacks in the current or another thread, by passing it Messages. the Message holds data to be used from the callback. a Handler.Callback can be passed to the constructor of Handler in order to avoid extending Handler directly. thus, to execute some code via callback from the current thread:
Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>
Callback callback = new Callback() {
public boolean handleMessage(Message msg) {
<code to be executed during callback>
}
};
Handler handler = new Handler(callback);
handler.sendMessage(message);
EDIT: just realized there's a better way to get the same result (minus control of exactly when to execute the callback):
post(new Runnable() {
#Override
public void run() {
<code to be executed during callback>
}
});
You can also use LocalBroadcast for this purpose. Here is a quick guide
Create a broadcast receiver:
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter("speedExceeded"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
Double currentLatitude = intent.getDoubleExtra("latitude", 0);
Double currentLongitude = intent.getDoubleExtra("longitude", 0);
// ... react to local broadcast message
}
This is how you can trigger it
Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
unRegister receiver in onPause:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
asume the main function is the activity that is triggering the event:
fun main() {
val worker = Worker()
worker.setOnCallListener(
object: OnCallListener {
override fun onCall() {
// here we define what should happen
// when the event accures
print("event happend")
}
}
)
// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}
the Worker class has an instance of Type OnCallListener interface
and a method to set its value:
class Worker() {
lateinit var listener: OnCallListener
fun setOnCallListener(listener: OnCallListener) {
this.listener = listener
}
}
and the OnCallListener interface looks like this:
interface OnCallListener {
fun onCall()
}