I'm setting up my first Vaadin application with Vaadin 7.5.6 and the official Vaadin Spring 1.0.0. I want to use the MVP pattern but I'm asking myself how the components work together. Because I'm new to MVP i don't want to use any Addons, so i tried to set it up by myself.
So if I'm right, the LoginViewPresenter will give me the view over presenterInstance.getView(). This is already working fine, but how should i access to the presenter over the view? When i want to do a logic operation for my view i should do it in the presenter class. But how to call a presenter method from a view Buttonclicklistener?
My second question is if I have the UIScope annotation over my presenter class, when does Spring instantiate a new object from this class? I thougt as long as the UI exists. But after generating a random string in the constructor I'm printing out the content of the randomString variable (in the UI.class init() method) but there is always a new value.
Regards
LoginViewPresenter.java
#SpringComponent
#UIScope
public class LoginViewPresenter implements Serializable
{
private static final long serialVersionUID = 6286518141570430211L;
#Autowired
private LoginView view;
public final String randomString;
public LoginViewPresenter()
{
randomString = Utils.generateRandomString(8);
}
#PostConstruct
public void init()
{
}
public LoginView getView()
{
return view;
}
public void setView(LoginView view)
{
this.view = view;
}
}
LoginView.java
#SuppressWarnings("serial")
#UIScope
#SpringView(name = LoginView.NAME)
public class LoginView extends VerticalLayout implements View
{
public static final String NAME = "LoginView";
#PostConstruct
private void init()
{
}
#Override
public void enter(ViewChangeEvent event)
{
}
}
Your view should'nt be aware of the presenter. It should fire events, and your presenter can listen to them.
Here is how I do it:
LoginView.java
#SuppressWarnings("serial")
#UIScope
#SpringView(name = LoginView.NAME)
public class LoginView extends VerticalLayout implements View {
public static final String NAME = "LoginView";
#Autowired
private transient Collection<LoginViewListener> loginViewListeners;
#PostConstruct
private void init() {
...
Button b = new Button("click me");
b.addClickListener(e -> loginViewListeners.forEach(l -> l.eventFired()));
addComponent(b);
...
loginViewListeners.forEach(listener -> listener.viewInitialized(this));
}
#Override
public void enter(ViewChangeEvent event)
{
}
public interface LoginViewListener {
void viewInitialized(LoginView view);
void eventFired();
}
}
LoginViewPresenter.java
#SpringComponent
#UIScope
public class LoginViewPresenter implements LoginViewListener, Serializable {
private static final long serialVersionUID = 6286518141570430211L;
private LoginView view;
public final String randomString;
public LoginViewPresenter() {
randomString = Utils.generateRandomString(8);
}
#PostConstruct
public void init() {
}
public LoginView getView() {
return view;
}
public void setView(LoginView view) {
this.view = view;
}
#Override
public void viewInitialized(LoginView v) {
setView(v);
}
#Override
void eventFired() {
...
}
}
Does your randomString still have always a new value with this design?
Related
I have created a custom UI component TextInputPro that renders an EditText. I also have a native module that I can send events like focus() to.
My files are
|-- TextInputProPackage.java <--- packages all together
|-- TextInputProManager.java <--- creates a view (EditText)
|-- TextInputProModule.java <--- gets events from JS
How can TextInputProModule.java communicate with the View created by TextInputProManager.java?
TextInputProPackage.java:
public class TextInputProPackage implements ReactPackage {
#Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
// MODULE CREATED HERE
return Arrays.<NativeModule>asList(
new TextInputProModule(reactContext)
);
}
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
//VIEW CREATED HERE
return Arrays.<ViewManager>asList(
new TextInputProManager()
);
}
}
TextInputProManager.java:
public class TextInputProManager extends SimpleViewManager<EditText> {
public static final String REACT_CLASS = "TextInputPro";
public EditText editText;
#Override
public String getName() {
return REACT_CLASS;
}
#Override
public EditText createViewInstance(ThemedReactContext context){
editText = new EditText(context);
editText.setText("hello world");
return editText;
}
}
TextInputProModule.java:
public class TextInputProModule extends ReactContextBaseJavaModule {
public static final String REACT_CLASS = "TextInputPro";
private static ReactApplicationContext reactContext = null;
public TextInputProModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
#Override
public String getName() {
return REACT_CLASS;
}
#ReactMethod
public void focus () {
// WHERE ARE YOU EDITTEXT?
}
}
In my head, I have to make a global variable in Package.java and pass it down to both constructors, but I couldn't make that work
Let's say I have the following interfaces:
public interface MvpView { }
public interface MvpPresenter<V extends MvpView> {
void attach(V view);
}
Then a base class that implements MvpPresenter
public class BasePresenter<V extends MvpView> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
}
And finally the following class that extends BasePresenter
public abstract class BaseFragment<P extends MvpPresenter> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
While this compiles it's not safe because there is no guarantee that implementations of the class will implement the correct MvpView (the one that P presenter uses). In order to be safe I've left out the code of someMethod and every implementation must fill it with the exact same code presenter.attach(this)
What I'd like to do but I don't know how is something like the following:
public abstract class BaseFragment<V extends MvpView, P extends MvpPresenter<V>> implements V {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
Is there any way to achieve this?
Declare BaseFragment like this.
abstract class BaseFragment<P extends MvpPresenter<R>, R extends BaseFragment<P,R>> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(getThis());
}
abstract R getThis();
}
}
and use the extensions like this,
class MyFragment extends BaseFragment<MvpPresenter<MyFragment>, MyFragment> {
#Override
MyFragment getThis() {
return this;
}
}
This compiles:
public abstract class BaseFragment<P extends MvpPresenter<BaseFragment>> implements MvpView {
P presenter;
public void someMethod() {
presenter.attach(this);
}
}
But I haven't found a way to actually subclass it. I am not sure if MvpView is actually required for anything. Maybe something like this will be useful:
public interface MvpPresenter<V> {
void attach(V view);
}
public class BasePresenter<V> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
}
public abstract class BaseFragment<R extends BaseFragment> {
MvpPresenter<R> presenter;
public void someMethod() {
presenter.attach(getThis());
}
abstract R getThis();
}
public class MyBaseFragment extends BaseFragment<MyBaseFragment> {
#Override
MyBaseFragment getThis() {
return this;
}
}
Based on boobalan gnanasekaran and user158037 answers, I did it like this:
public interface MvpView { }
public interface MvpPresenter<V extends MvpView> {
void attach(V view);
}
public class BasePresenter<V extends MvpView> implements MvpPresenter<V> {
private V view;
#Override
public void attach(V view) {
this.view = view;
}
public V getView() {
return view;
}
}
public abstract class BaseFragment<V extends MvpView, P extends MvpPresenter<V>> {
P presenter;
protected abstract V getThis();
public void someMethod() {
presenter.attach(getThis());
}
public P getPresenter() { return presenter; }
}
And an example implementation
public interface MoviesContract {
interface Presenter extends MvpPresenter<MoviesContract.View> {
void loadMovies();
}
interface View extends MvpView {
void onMoviesLoaded();
}
}
public class MoviesPresenter extends BasePresenter<MoviesContract.View>
implements MoviesContract.Presenter {
#Override
public void loadMovies() {
getView.onMoviesLoaded();
}
}
public class MoviesFragment extends BaseFragment<MoviesContract.View, MoviesContract.Presenter>
implements MoviesContract.View {
#Override
public MoviesContract.View getThis() {
return this;
}
public loadMovies() {
getPresenter().loadMovies();
}
#Override
public void onMoviesLoaded() {
//
}
}
I have multiple ViewHolders that work as separated views inside a vertical RecyclerView. I'm practicing with the new Architecture Components ViewModel.
Inside my ViewModel I have a couple of MutableLiveData lists that i want to observe, but how can I call
ViewModelProviders.of((AppCompatActivity)getActivity()).get(FilterViewModel.class)
and
mFilterViewModel.getCountries().observe(this, new Observer<ArrayList<TagModel>>() {
#Override
public void onChanged(#Nullable ArrayList<TagModel> tagModels) {
}
});
without leaking the activity or save the activity inside the adapter?
my ViewModel
public class FilterViewModel extends ViewModel {
private final MutableLiveData<ArrayList<TagModel>> mCountries;
private final MutableLiveData<ArrayList<TagModel>> mSelectedCountryProvinceList;
private final MutableLiveData<ArrayList<TagModel>> mDistanceList;
public FilterViewModel(){
mCountries = new MutableLiveData<>();
mSelectedCountryProvinceList = new MutableLiveData<>();
mDistanceList = new MutableLiveData<>();
TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
#Override
public void update(TagSearchList object) {
mCountries.setValue(object.getCountries());
}
#Override
public void update(int id, TagSearchList object) {
if (id == 5){
TagStore.getInstance().unSubcribe(this);
update(object);
}
}
#Override
public void error(String error) {
}
}).get(5,"parent");
TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
#Override
public void update(TagSearchList object) {
mSelectedCountryProvinceList.setValue(object.toList());
}
#Override
public void update(int id, TagSearchList object) {
if (id == 6){
TagStore.getInstance().unSubcribe(this);
update(object);
}
}
#Override
public void error(String error) {
}
}).get(6,"parent");
TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
#Override
public void update(TagSearchList object) {
mDistanceList.setValue(object.toList());
}
#Override
public void update(int id, TagSearchList object) {
if (id == 51){
TagStore.getInstance().unSubcribe(this);
update(object);
}
}
#Override
public void error(String error) {
}
}).get(51,"parent");
}
public void selectCountry(final TagModel country){
TagStore.getInstance().subscribe(new StoreObserver<TagSearchList>() {
#Override
public void update(TagSearchList object) {
mSelectedCountryProvinceList.setValue(object.toList());
}
#Override
public void update(int id, TagSearchList object) {
if (id == country.getId()){
TagStore.getInstance().unSubcribe(this);
update(object);
}
}
#Override
public void error(String error) {
}
}).get(country.getId(),"parent");
}
public LiveData<ArrayList<TagModel>> getCountries(){
return mCountries;
}
public LiveData<ArrayList<TagModel>> getDistances(){
return mDistanceList;
}
public LiveData<ArrayList<TagModel>> getProvinces(){
return mSelectedCountryProvinceList;
}
I am using Room Persistence library. Below is my code for recyclerview adapter using MVVM.
You can see CartViewModel and I have initialized it into the constructor. The constructor gets the context from the activity, and I have cast it into FragmentActivity.
private CartViewModel cartViewModel;
public CartListAdapter(Context context, List<CartModel> cartModels) {
this.context = context;
this.cartModels = cartModels;
cartViewModel = ViewModelProviders.of((FragmentActivity) context).get(CartViewModel.class);
}
Here is my full adapter class. I hope it will help.
public class CartListAdapter extends RecyclerView.Adapter<CartListAdapter.CartListViewHolder> {
private static final String TAG = "CartListAdapter";
private Context context;
private List<CartModel> cartModels;
private Double totalQuantity = 0.0;
private CartViewModel cartViewModel;
public CartListAdapter(Context context, List<CartModel> cartModels) {
this.context = context;
this.cartModels = cartModels;
cartViewModel = ViewModelProviders.of((FragmentActivity) context).get(CartViewModel.class);
}
#NonNull
#Override
public CartListViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new CartListViewHolder(LayoutInflater.from(context).inflate(R.layout.list_all_cart_item,parent,false));
}
#Override
public void onBindViewHolder(#NonNull CartListViewHolder holder, int position) {
CartModel cartModel = cartModels.get(position);
Glide.with(context)
.load(cartModel.getPPICLocate())
.into(holder.cartItemImage);
holder.tvCartProductName.setText(cartModel.getProductName());
holder.tvCartProductCategory.setText(cartModel.getPCategorySubID());
holder.tvCartProductPrice.setText(cartModel.getPPriceSales());
holder.etCartProductQuantity.setText(cartModel.getPQuantity());
holder.btnCartPQtIncrease.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
totalQuantity = Double.valueOf(holder.etCartProductQuantity.getText().toString());
totalQuantity = totalQuantity+1;
cartModel.setPQuantity(totalQuantity.toString());
updateCart(cartModel);
}
});
holder.btnCartPQtDecrease.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
totalQuantity = Double.valueOf(holder.etCartProductQuantity.getText().toString());
totalQuantity = totalQuantity-1;
cartModel.setPQuantity(totalQuantity.toString());
updateCart(cartModel);
}
});
}
#Override
public int getItemCount() {
return cartModels.size();
}
public class CartListViewHolder extends RecyclerView.ViewHolder{
private ImageView cartItemImage;
private TextView tvCartProductName,tvCartProductCategory,tvCartProductPrice,
etCartProductQuantity,tvCartProductPrevPrice;
private ImageButton btnCartPQtIncrease,btnCartPQtDecrease;
public CartListViewHolder(#NonNull View itemView) {
super(itemView);
cartItemImage= itemView.findViewById(R.id.cartItemImage);
tvCartProductName= itemView.findViewById(R.id.tvCartProductName);
tvCartProductCategory= itemView.findViewById(R.id.tvCartProductCategory);
tvCartProductPrice= itemView.findViewById(R.id.tvCartProductPrice);
etCartProductQuantity= itemView.findViewById(R.id.etCartProductQuantity);
tvCartProductPrevPrice= itemView.findViewById(R.id.tvCartProductPrevPrice);
btnCartPQtIncrease= itemView.findViewById(R.id.btnCartPQtIncrease);
btnCartPQtDecrease= itemView.findViewById(R.id.btnCartPQtDecrease);
}
}
public void addItems(List<CartModel> cartModels) {
this.cartModels = cartModels;
notifyDataSetChanged();
}
private void updateCart(CartModel cartModel){
String tqt = String.valueOf(cartModel.getPQuantity());
Log.d(TAG, "updateQuantity: "+tqt);
/*cartRepository.updateCartRepo(cartModel);*/
cartViewModel.updateCartItemVM(cartModel);
}
}
You could create an OnClickListener interface instead, then implement the onClick method (defined in your interface) in your fragment or activity where you have access to your view model.
My solution to this issue is;
create a new variable(ViewModel) for the layout (fragment or activity layout)
in your activity or fragment get the instance of your viewModel with ViewModelProviders
hold the data which fills your recyclerView in MutableLiveData and observe it and fill the adapter of recyclerView with the value you observed
here you mut be careful not to create adapter instance in observe method.
if you create it in observe method, it will make leaks
I'm having the following classes/interfaces:
public class GenericViewModel<T extends AbstractDatabaseObject> {
private Class<?> type;
#SuppressWarnings("unchecked")
public GenericViewModel(Class<?> cl) {
type = cl;
}
}
and a specialization:
public class PersonViewModel extends GenericViewModel<Person> implements IPersonViewModel{
public PersonViewModel() {
super(Person.class);
}
}
Now, my problem is in the presenter:
public class GenericPresenter implements IGenericView.IGenericViewListener {
private GenericViewModel<AbstractDatabaseObject> model;
private IGenericView view;
public GenericPresenter(GenericViewModel<AbstractDatabaseObject> model, IGenericView view) {
this.model = model;
this.view = view;
view.addListener(this);
}
}
To be more precise, I cannot call the constructor of the super class with the given arguments:
public class PersonPresenter extends GenericPresenter {
PersonViewModel model;
IPersonView view;
public PersonPresenter(PersonViewModel model, IPersonView view) {
super(model, view); // Here is the problem. No such constructor in superclass found
// IGenericView i = (IGenericView) view; <-- this seems to work
// GenericViewModel<AbstractDatabaseObject> m = model; <-- this doesn't
}
}
What do I have to change?
Try to change the GenericPresenter class in this way:
private GenericViewModel<? extends AbstractDatabaseObject> model;
private IGenericView view;
public GenericPresenter(GenericViewModel<? extends AbstractDatabaseObject> model,
IGenericView view) {
this.model = model;
this.view = view;
view.addListener(this);
}
Is there a standard set of Listener/Observer/Observable classes in Android for managing application events in Android?
I'm not talking about UI or other Android API events, but rather custom app events like GameOverEvent, LevelClearedEvent, etc.
Is there a preferred interface to implement/extend so that I can implement things like:
public void addGameOverListener(GameOverListener listener)
It's easy,, you just need to create your own EventListener
public interface onGameFinishedListener {
public void onGameFinished(GameView gameView);
}
and some class which has onGameFinished() method
public abstract class GameView extends SurfaceView implements SurfaceHolder.Callback{
List<onGameFinishedListener> listeners;
public GameThread gameThread;
protected int width;
protected int height;
public GameView(Context context) {
super(context);
width = 320;
height = 480;
listeners = new ArrayList<onGameFinishedListener>();
}
public abstract void init();
public void registerGameFinishedListener(onGameFinishedListener listener) {
listeners.add(listener);
}
protected void GameFinished(GameView gameView) {
for (onGameFinishedListener listener : listeners) {
synchronized(gameThread.getSurfaceHolder()) {
listener.onGameFinished(gameView);
}
}
}
}
and then you implement the onGameFinishedListener in your activity or view which you want to do operation when the game finish,
public class RocketActivity extends GameActivity implements onGameFinishedListener {
private final int MENU = 0;
private final int END = 1;
private final int CONFIRMATION = 2;
private RelativeLayout layout;
private RocketView rocketView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
layout = new RelativeLayout(this);
rocketView = new RocketView(this);
rocketView.registerGameFinishedListener(this);
rocketView.init();
layout.addView(rocketView);
setContentView(layout);
}
#Override
public void onGameFinished(GameView gameView) {
runOnUiThread(new Runnable() {
#Override
public void run() {
showDialog(END);
}
});
}
}
there. no need to rely on Android for EventListener. :)
Have you tried EventBus by GreenRobot?
It is basically a pretty standard implementation of an eventBus for handling application wide events.
It provides inter-thread communication which is quite neat.
Pretty similar to what you get for GWT