Search code examples
androidnullfindviewbyidandroid-annotations

Android annotations does not know textview


I got a custom component (something like information panel) - LinearLayout with few of textviews. I am using android annotations.

PROBLEMS
If i try to use @AfterViews, that method is never called (problem 1).
Problem is when i try to set something in the textview through annotations, that textview is null (problem 2)..

If i try that from another class, where i am sure android annotations works, i can easy and without any problem get on that textView and set there something..The variable is not null and there are no problems..BUT in another classes where i use AA, i use Activity as well..:/ so i am thinking if there is problem in @EViewGroup.. But according to their javadoc, @EVIewGroup has no problem with AfterViews and ViewById..(according to their sites, fast everything is viewGroup :] and i think i select the right possibility for extending from linearLayout)

Does someone have some tip what i am missing?

-- I use that custom component in one more fragments.
-- There is one class calling method in this class (--> in code preview -- somethingIsCalling()), and that method is fired when something want to display in the textView)

Problem code:

Custom component layout (info_panel.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:baselineAligned="false"
    android:id="@+id/infoPanel">
    <TextView
        android:id="@+id/txtAccName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="right|center_vertical" />
</LinearLayout>

java code for that custom component:

@EViewGroup(R.layout.info_panel)
public class InfoPanel extends LinearLayout {
    @ViewById(R.id.txtAccName)
    protected TextView txtName;

        public InfoPanel(Context ctx, AttributeSet attr)
    {
        super(ctx, attr);
        LayoutInflater.from(ctx).inflate(R.layout.info_panel, this);
    }

    @AfterViews
    protected void init() 
    {
            // PROBLEM 1, THIS METHOD IS NEVER CALLING
        txtName.setText("call after view");
    }

    public void somethingIsCalling()
    {
            // PROBLEM 2, txtName WHEN ITS CALLED IS NULL
            txtName.setText("something is calling");
    }
}

Solution

  • Here is how I would rewrite your code to get it work:

    layout_info_panel.xml

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <TextView
            android:id="@+id/txtAccName"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right|center_vertical" />
    </merge>
    

    Note how you can replace root <LinearLayout/> with <merge/> here but still be able to use all <LinearLayout/> specific attributes (e.g. layout_weight="1") (https://github.com/excilys/androidannotations/wiki/Enhance-custom-views#how-to-create-it-)
    It is possible because your custom class InfoPanel extends LinearLayout.
    As a bonus you can remove 1 level of hierarchy, which is always small performance boost (http://www.curious-creature.org/2012/12/01/android-performance-case-study/)

    InfoPanel.java

    @EViewGroup(R.layout.info_panel)
    public class InfoPanel extends LinearLayout {
        @ViewById(R.id.txtAccName)
        protected TextView txtName;
    
        public InfoPanel(Context ctx, AttributeSet attr) {
            super(ctx, attr);
            // remove this inflation, AndroidAnnotations does this for you in @EViewGroup(R.layout.info_panel)
            // LayoutInflater.from(ctx).inflate(R.layout.info_panel, this);
        }
    
        @AfterViews
        protected void init() {
                // PROBLEM 1 would be fine now
            txtName.setText("call after view");
        }
    
        public void somethingIsCalling() {
                // PROBLEM 2 also will be resolved IF you call it after @AfterViews method has done binding.
                txtName.setText("something is calling");
        }
    }

    Now wherever you want to include your InfoPanel in xml, MAKE SURE to include generated class (one with _underscore), otherwise your fields will indeed always be null, i.e.

    EDIT: apparently in this case this was causing issues for OP and changing <com.stackoverflow.answers.InfoPanel> to <com.stackoverflow.answers.InfoPanel_> solved fields being null.

    layout_ordinary_fragment.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical" >
    
        <com.stackoverflow.answers.InfoPanel_
            android:id="@+id/infoPanelView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        …
    </LinearLayout>
    

    (https://github.com/excilys/androidannotations/wiki/Enhance-custom-views#how-to-use-it-)
    I advise you to go through all Cookbook in AndroidAnnotations wiki, there are couple of more common pitfalls over there.