Skip to content
This repository was archived by the owner on Feb 26, 2023. It is now read-only.
This repository was archived by the owner on Feb 26, 2023. It is now read-only.

Regarding use of @Ebean in Fragment #331

Description

@anandinnz

Hi,

I was hoping to use the EBean in fragment in this fashion

In the fragment

@EFragment(R.layout.contacts)
public class BuddyListFragment extends SherlockFragment{



    @Bean
    BuddyListFragmentViewHolder viewHolder;

    @Bean
    BuddyListFragmentModel model;

    @ViewById(R.id.listView) ExpandableListView listView;


    @AfterInject
    void afterViews()
    {
        viewHolder.setModel(model);     

    }

In the ViewHolder

@EBean
public class BuddyListFragmentViewHolder {


    @RootContext 
    Context context;

    @ViewById(R.id.listView) ExpandableListView listView;
    @ViewById(R.id.progressbar_default) ProgressBar progressBar;

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?

Activity

  1. pyricau commented on Oct 9, 2012

    @pyricau
    Contributor

    HI @anandinnz

    the context of Ebean is the activity not the fragment.

    In fact, a Fragment is not a Context, 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 findViewById on 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 ?

  2. anandinnz commented on Oct 9, 2012

    @anandinnz
    Author

    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);

  3. pyricau commented on Oct 10, 2012

    @pyricau
    Contributor

    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.

  4. pyricau commented on Nov 25, 2012

    @pyricau
    Contributor

    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 HasView has a OnViewChangedNotifier instance that registers and holds the OnViewChangedListeners listener to view changes in this HasView.

    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 OnViewChangedNotifier when 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 HasView would also be identical.

  5. pyricau commented on Nov 25, 2012

    @pyricau
    Contributor

    One thing to take care of though: no injection of views in @Ebean in scope singleton and in @EApplication.

  6. ghost assigned on Feb 27, 2013
  7. added a commit that references this issue on Mar 3, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions