Repository navigation
Regarding use of @Ebean in Fragment #331
Description
Activity
HI @anandinnz
the context of Ebean is the activity not the fragment.In fact, a
Fragmentis not aContext, so that's quite normal :) .The first thing to notice is that a fragment is not really a real android component. A fragment adds its views to the activity view hierarchy. So when you call
findViewByIdon an activity, if the fragment is attached, you can find views belonging to the fragment.If the context given to the eBean is an activity, then when
afterSetContentView_(check the generated code) is called the views are injected.I think your use case should work, maybe there's some bug lurking somewhere... could you use the debug in the generated code and see if the injected context in BuddyListFragmentViewHolder is an activity ? And see if the method that does the injection is called ?
Thanks @pyricau . You guys have setup a fantastic library btw.
The injected context in ViewHolder is activity as you suggest. If I go through debug mode in the ViewHolder it does use the activity to call findViewById and the activity does exist when I go through debug mode, but the ProgressBar and the listView are null there still, as I guess the id I am looking for is part of the fragment's UI R.layout.contacts.
It works in the fragment, because the fragment first inflates the layout.contacts
if (contentView_ == null) {
contentView_ = inflater.inflate(layout.contacts, container, false);
}and then uses it call by findViewById
public View findViewById(int id) {
if (contentView_ == null) {
return null;
}
return contentView_.findViewById(id);In fact, I think you spotted a bug. We should call findViewById on the view of the fragment, not directly on the activity. In fact, I don't like the way it's currently implemented in AA. Instead of having a delegation of afterSetContentView calls and trying to cast the context as an activity to call findViewById, we should instead have the beans register to a "ViewContainer", that would call them back when needed, passing in the root view.
Regardless of #352, I think we can fix that problem in a quite straight way.
We assume view injection always happens on the main thread (this is mandatory but up to the user).
Here are my thoughts (code written within GitHub, it may not compile):
All generated components that have a content view (activity, fragment, view, viewgroup) would implement:
public interface HasView { View findViewById(int id); }
All generated components that require view injection / binding would implement:
public interface OnViewChangedListener { void onViewChanged(HasView hasView); }
and inject views / bindings in
onViewChanged(). This applies for activities, fragments as well. This would also make the code more coherent.Each class implementing
HasViewhas aOnViewChangedNotifierinstance that registers and holds theOnViewChangedListeners listener to view changes in thisHasView.public class OnViewChangedNotifier { // TODO discuss whether List or Set is more appropriate, and which implementation private final List<OnViewChangedListener> listeners = new ArrayList<OnViewChangedListener>(); public void notifyViewChanged(HasView hasView) { for(OnViewChangedListener listener : listeners) { listener.onViewChanged(hasView); } } public void register(OnViewChangedListener listener) { listeners.add(listener); } }
We need a static holder for the current
OnViewChangedNotifierwhen building the dependency graph.public class OnViewChangedNotifier { private static OnViewChangedNotifier currentNotifier; public static OnViewChangedNotifier getCurrentNotifier() { return currentNotifier; } public static OnViewChangedNotifier setCurrentNotifier(OnViewChangedNotifier notifier) { this.currentNotifier = notifier; } // TODO discuss whether List or Set is more appropriate, and which implementation private final List<OnViewChangedListener> listeners = new ArrayList<OnViewChangedListener>(); public void notifyViewChanged(HasView hasView) { for(OnViewChangedListener listener : listeners) { listener.onViewChanged(hasView); } } public void register(OnViewChangedListener listener) { listeners.add(listener); } }
Here is what the generated code would look like for an activity. You may notice weird repetitions, but this is to have identical generated code everywhere (see below).
public class MyActivity_ extends MyActivity implements HasView, OnViewChangedListener { private OnViewChangedNotifier onViewChangedNotifier = new OnViewChangedNotifier(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(); OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.getCurrentNotifier(); OnViewChangedNotifier.setCurrentNotifier(onViewChangedNotifier); OnViewChangedNotifier.getCurrentNotifier().register(this); // inject dependencies OnViewChangedNotifier.setCurrentNotifier(previousNotifier); setContentView(R.layout.my_layout); } @Override public void setContentView(int layoutResId) { super.setContentView(layoutResId); onViewChangedNotifier.notifyViewChanged(this); } @Override public void onViewChanged(HasView hasView) { myTextView = (TextView) hasView.findViewById(R.id.some_text_view); hasView.findViewById(R.id.some_button).setOnClickedListener(new OnClickListener()) { @Override public void onClicked(View view) { someButtonClicked(); } } } }
And for a random bean:
public class MyBean_ extends MyBean implements OnViewChangedListener { private MyBean_() { OnViewChangedNotifier.getCurrentNotifier().register(this); // inject dependencies } @Override public void onViewChanged(HasView hasView) { myTextView = (TextView) hasView.findViewById(R.id.some_text_view); hasView.findViewById(R.id.some_button).setOnClickedListener(new OnClickListener()) { @Override public void onClicked(View view) { someButtonClicked(); } } } }
The core idea is that the code to inject views and bind listeners would be identical wherever generated. And the code for each
HasViewwould also be identical.One thing to take care of though: no injection of views in
@Ebeanin scope singleton and in@EApplication.- added a commit that references this issue
on Mar 3, 2013
Hi,
I was hoping to use the EBean in fragment in this fashion
In the fragment
In the ViewHolder
I guess in the current version this is not possible as the context of Ebean is the activity not the fragment.
Do you have any suggestions / work arounds for me in this case if I want to use this pattern of implementation.
Or do you have any other recommendations to implement this differently in fragments?