I have a custom View class that extends Spinner. I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection. I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow, or if I should just talk directly to the context (for some reason that doesn't feel safe, even though I can't think of a way in which it might fail, offhand).
the right way to do that, is to "listen" to your custom view by exposing an interface which your view holding a reference to instance of him, and you hosting activity should implement. exactly like the OnItemSelected interface and any events which android views are exposing is been implemented. this is the observer design pattern.
for example:
public class MyCustomSpinner extends Spinner {
public MyCustomSpinner(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public interface IMyEventListener {
public void onEventOccurred();
}
private IMyEventListener mEventListener;
public void setEventListener(IMyEventListener mEventListener) {
this.mEventListener = mEventListener;
}
protected void someMethodWhichDoingSomthingAndShouldRaiseAlsoTheEvent() {
/*
* Some Code which the function doing //more code...
*/
if (mEventListener != null) {
mEventListener.onEventOccurred();
}
}
}
this is how you will use it from your activity:
mMyCustomSpinner.setEventListener(new IMyEventListener() {
#Override
public void onEventOccurred() {
// TODO Auto-generated method stub
}
});
I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection.
You don't want to "talk to the Activity that it's embedded in". You want to talk to the controller responsible for the View. Today, that might be an Activity. Tomorrow, that might be a Fragment.
I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow
That implies that the View knows the specific type of Adapter, since the Adapter interface does not have any sort of getContext() method. Moreover, it ties you to talking to the Activity, which is not a good plan at this point, as mentioned above.
Personally, I'm a bit dubious about having a custom Spinner subclass in the first place. But, assuming there's a good reason for it, you should follow Tal Kanel's advice (posted while I was writing this) and design a custom listener interface for this custom event that is being declared by your custom View. Have the controller (Activity or Fragment) supply an implementation of that interface -- this could be directly implemented on the controller, or implemented as an anonymous inner class (as in Tal Kanel's answer), etc. Have your custom View call method(s) on the listener interface as needed.
The correct way is using a listener of some sort. I think you can make direct reference, your code would just not be reusable for another project then...
A simple solution -
((ParentClass) context).functionToRun();
Where ParentClass is the class name of the activity.
Related
I have this simple problem:
An activity contains a fragment. Both want to communicate with each other.
The fragment wants to inform the activity about certain interactions. Therefore it defines the OnFragmentInteractionListener interface which is implemented by the activity. This is already part of the Android Studio fragment template, so nothing new here.
The activity wants to inform the fragment about the back button being pressed. Therefore it defines the OnBackPressedListener interface which is implemented by the fragment.
class MainActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener {
...
#Override
public void onFragmentInteraction(Uri uri) {
...
}
interface OnBackPressedListener {
boolean onBackPressed();
}
}
class MyFragment extends Fragment implements MainActivity.OnBackPressedListener {
...
#Override
public boolean onBackPressed() {
...
}
interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
IMHO a simple design that, unfortunatley, doesn't work. "Cyclic inheritance involving ..." is the message from Android Studio.
I would have expected that compilation error if I used nested classes instead of interfaces as the classes' static initializers might be cyclic dependent on each other. I don't see that problem with interfaces but I'm sure there's a good reason why it's not allowed.
The easiest way to resolve this is making one of the interfaces a package-level interface. But I wonder if this is the way to go. And if so: which one should be package-level.
You can have a BroadcastListener attached to the Fragment using a register call.
Please do not forget to unregister the listener in onStop or onPause if you plan to interact with the UI or it will cause exceptions.
Now the simple way is, override the onBackPressed inside your activity and when it gets hit, broadcast an event and let your fragment know. Simple and shouldn't be too problematic to handle.
I personally prefer this approach to send messages to all my Fragments. Another approach as you yourself said is declaring a package wide interface and doing the stuff that you wanted as you already wrote. Nothing wrong with this either, it's just Java being Java.
If MVVM is all about data binding and cannot do something like view.doThis(), otherwise it's MVP, then how to invoke actions on views?
Suppose I have view that has a snackbar. View is controlled by its ViewModel. How is this ViewModel supposed to show snackbar without going snackbar.show()?
In MVVM, ViewModel captures the state of the view. View observes the ViewModel for changes and updates itself. Thus, the communication between View & ViewModel happens through change of values (as against method calls in MVP).
Since Snackbar is like a global behaviour (like Toast), it can be implemented at the Activity/Fragment level. So, you can make a MessageHelper interface and pass it to the ViewModel as a dependency. Activity will implement it and display Snackbar.
Example:
ItemViewModel that consumes the interface
Activity base class that implements the interface
However, its possible that there is some view specific behaviour which cannot be implemented at the Activity level. For such cases, you can make use of databinding.Observable to trigger an event. For example, lets say we want to animate a particular view. We can create a BindingAdapter
#BindingAdapter({"shakeTrigger"})
public static void showSnackbar(View view, Void trigger) {
// Do the animation here. You could add meaningful argument types to control the animation
}
In XML, we can apply this using
<TextView
bind:shakeTrigger="#{vm.shakeTrigger}"/>
Then, in the viewModel, you can trigger the shake using Data Binding apis. One way using BaseObservable can be:
public class ConfigurationViewModel extends BaseObservable implements ViewModel {
#Bindable
public final Void shakeTrigger = null;
public void shake() {
notifyPropertyChanged(BR.shakeTrigger);
}
}
If you use RxJava, the trigger could be implemented from rx.Observable. You can checkout my library to use RxJava with Data Binding.
https://github.com/manas-chaudhari/android-mvvm
The short answer is you don't and that's actually a good thing. In MVVM ViewModel is responsible for preparing and storing data for the view. So it gets the data from the model and makes it ready to be set on the view but it doesn't set the value, Setting the value and updating view states are responsibilities of the view itself, View in MVVM watches for changes in data and updates itself.
An example of this would be showing an empty list page when your list is empty. To do this in MVVM, you define a state for the view visibility in your ViewModel lets call it emptyPageVisibility and then update this value appropriately.
public class PlaylistDetailViewModel extends ViewModel {
private MutableLiveData<Integer> emptyPageVisibility = new MutableLiveData<>();
private void someMethodInYourViewModel(){
emptyPageVisibility.setValue(View.VISIBLE);
}
}
Then inside your view, you observe this and update the view when this data is changed like this
viewModel.getEmptyPageVisibility().observe(this,
visibility -> emptyPageView.setVisibility(visibility));
Before some time, i started looking for a pattern to decouple UI from a logic of my app. I decided to use MVP, but there is one significant problem which i cant solve.
How can i inject a instance of presenter into view, if classes that implements Application, are launched from static method. There is also no choice to launch specific instance of class implementing Application, so parameters in constructor are useless.
Also i do not use FXML, my view class is coded in java.
PS: Sorry for my english, as it's not my native language
You can pass a reference from say Main.java to a Presenter. In Main do this:
Presenter p = new Presenter(); // This is your Presenter class
p.setReference(this); // Call a method in the presenter
// and here is a method in Main.java just as an example
public StackPane getRootView(){
return this.rootView;
}
Then in Presenter you have:
private Main main;
public void setReference (Main main) {
this.main = main;
}
Your presenter can now call methods in Main e.g.
StackPane sp = main.getRootView();
You could also do this in the constructor of Presenter.
I have written a sample code to answer to this problematic.
https://github.com/oterrien/JavaFX_Presenter.git
The view interface provides the intention of the view.
For example:
getting and setting text
getting and setting result1
getting and setting result2
getting and setting event handler for the Add button
The concret view is created from FXML file. Each field of the control is defined with #FXML. The action to be triggered when the button is clicked is also a method and is prefixed by #FXML.
The concret view implements the interface by providing a mapping between #FXML fields and getting/setting methods. And the triggered method does just call the event handler.
The concret view is also responsible of creating the presenter (which refers itself as view).
That is the important point. The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view.
For that purpose, the presenter should be able to call the view in order to set data and retrieve data once updated by user. That is why, the presenter contains a reference of the view. But it should also provide action to be done when view's event handlers are called.
When a user clicks on button "Add", the method which is bound with FXML is called. This method call the EventHandler which has been set by the presenter. In other words, the presenter is responsible of registering its own method to the view's EventHandler.
Finally, testing the presenter just consists in creating a mock of the view.
I am new to Java/Android programming and unfortunately I don't fully under stand the logic behind this piece of code:
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
I have alredy read a whole lot of tutorials about it but unfortunately in no one so far the code gets explained in detail. Maybe because it's so basic that every decent object oriented programmer would understand the reason for the structure right away.
I am wondering why do I need to use new View.OnClickListener()as a parameter? for the setOnClickListener method. In other words why doesn't something like
button.setOnClickListener(
public void onClick(View v) {
// Perform action on click
});
this work?
Besides that I am not quite sure why the onClick method requires the paramterer of View v.
I would be quite thankful for some help since I am currently rather puzzled.
View.OnClickListener is an interface which you need to implement when you want to handle click events. In your code, you are doing that by doing new View.OnClickListener(). Here you are actually creating an anonymous class that implements View.OnClickListener. And any class that implements View.OnClickListener must also implement all the methods declared in it (e.g. the onClick method). Also, in public void onClick(View v) {..},
the View v denotes the view or the button that was clicked so that you can perform whatever you want with it. For example, get it's id, change it's color etc.
Ok so first of all onClick method wants a parameter as a function. The thing is in Java you can't send a function as a parameter as you can do in other languages (like C++). The Java solution is to use a Functional Interface.
In this case OnClickListener is an interface that have one method you must implement : public void onClick(View v). View v it's the view you just clicked so the method it's called.
button.setOnClickListener(OnClickListener) means the set... function needs an argument that implements the OnClickListener.
So that what this code does :
new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
It is creating an anonymous class that implements the OnClickListener interface required.
Imagine the case where it would be like what you defined - you do not pass in a parameter and simply describe the actions to be taken when that button is pressed. It will work perfectly but for that button only. You will have to redo this method for every other button. What in the case where there are quite a few buttons that do the same thing? You will end up redefining it every time. Wouldn't it be easier to simply just do it once and then pass that in? Also wouldn't it be easier to simply just have one onClick() method which identifies the id of the element being clicked and then performs the logic just for the widget with that id rather than having laborious separate lines of code?
Also don't forget that the parameter being passed belongs to the View class which is the superclass of every other widget. So you can pass the onClickListener() method defined for other widgets into this method if you want (though this case is practically rare).
To answer your question, the Android team could have designed it to be the way you described but chose to do it this way due to best practices and programming paradignms.
As documentation says
Name of the method in this View's context to invoke when the view is clicked.
Perfect, but is it "somehow" possible to create a static method and use it everywhere I need, not to define inside each Activity class. And yes I'm aware of defining the method inside base Activity class. All I'm looking for is to just write full name of method and let it uses it. Possible ?
Thanks
You cannot assign a static method to the onClick xml property. You'll have to add the method you specify there to any activity that consumes the layout. You can have that method in each activity call a static method that actually handles the click, but there's no way to avoid having the instance methods altogether.
It's not possible. Android searches for this method in an activity instance. That is why it must belong to an instance.
My suggestion is you create a class that handles all the functions that the buttons does
and call that function on the Activity's onClick method.
Example
public class AppModel {
public static void button1Function(){
//do something
}
}
then on your activities
public void button1Clicked(View view){
AppModel.button1Function();
}
I think this looks like the MVC design paradigm. Click here for further reading.
Why don't you create an entire new class having your static method?
You can load this class wherever you want then...