Butterknife does not work with Include tag
Asked Answered
S

5

10

I have a CoordinatorLayout where I have my Button:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="bottom|end">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginBottom="16dp"
        android:layout_marginRight="16dp"
        android:src="@drawable/icon"
        app:fabSize="normal"/>

</android.support.design.widget.CoordinatorLayout>

And then I have the Activity xml where I include the Button:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    ...

        <include
            android:id="@+id/fabHere"
            layout="@layout/fab_here"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

</LinearLayout>

And here I have a fragment with Butterknife:

public class MyFragment extends Fragment {
....
@BindView(R.id.fab) FloatingActionButton fab;


    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            view = inflater.inflate(R.layout.my_fragment, container, false);
            ButterKnife.bind(this, view);

            return view;
    }
...
 }

But if I start the App this appears:
java.lang.IllegalStateException: Required view 'fab' with ID 2131624072 for field 'fabHere' was not found. If this view is optional add '@Nullable' (fields) or '@Optional' (methods) annotation.

I tried to add the '@Nullable' and '@Optional' annotations, but it does not work

Stoneham answered 22/11, 2016 at 12:25 Comment(8)
what happens if your remove android:id="@+id/fabHere" ?Anisometric
nothing happensObligation
What exactly do you mean nothing happens? Does it crash, does it not show anything? Use Butterknife.setDebug(true)Slobbery
The same error: java.lang.IllegalStateException: Required view 'fab' with ID 2131624072 for field 'fab' was not found. If this view is optional add '@Nullable' (fields) or '@Optional' (methods) annotation.Obligation
@SarahPöhler check this link github.com/JakeWharton/butterknife/issues/396Diagram
Clean and rebuild the project after removing the include tag id.Slobbery
For Butter Knife to be able to find the button in the fragment, wouldn't the button have to be in the layout file being inflated?Phillane
how can I get the the fab if I infalte it?Obligation
W
16

Simply remove the id from the include tag and the binding will work, even for the fields in the included layout.

Woodsman answered 9/2, 2018 at 0:7 Comment(5)
Yes, I saw that somewhere too ! This is the right fix !Photodisintegration
It will solve your issue. Also, check if you wrote (pasted) correct layout. In my case, I copy-pasted from another activity and was wondering why does the Butterknife don't see my views :)Disbud
but if there are some views below the included view whose position is dependent on the id of included view, then what shall be done?Charette
If you have the option/ability I'd recommend switching to Kotlin, which would get rid of the need for Butterknife. Short of that, you could consider wrapping the include tag in another tag, that has an ID.Woodsman
This issue was driving me nuts and such a simple fix. +1Ledeen
P
5

The MainActivity,

public MainActivity extends AppCompatActivity {
    // 1. First, we declare the layout that was included as a View objects.
    @BindView( R.id.layout_1 ) View layout_1;
    @BindView( R.id.layout_2 ) View layout_2;

    @Override
    protected void onCreate( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        // 2. In here, we bind the included layouts
        ButterKnife.bind( this );

        // 4. Then, we create objects of the type of the IncludedLayout.
        //      In this example the layout reuse the same layout twice, so, there are two
        //      IncludedLayouts.
        IncludedLayout includedLayout_1 = new IncludedLayout();
        IncludedLayout includedLayout_2 = new IncludedLayout();

        // 5. We bind the elements of the included layouts.
        ButterKnife.bind( includedLayout_1, layout_1 );
        ButterKnife.bind( includedLayout_2, layout_2 );

        // 6. And, finally, we use them.
        includedLayout_1.displayed_text.setText(  "Hello" );
        includedLayout_2.displayed_text.setText(  "Hey!" );
    }

    // 3. We create a static class that will be an container of the elements
    //     of the included layout. In here we declare the components that
    //     hold this. In this example, there is only one TextView.
    static class IncludedLayout {
        @BindView( R.id.displayed_text ) TextView displayed_text;
    }
}

The XML of the MainActivity:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <include android:id="@+id/layout_1" layout="@layout/included_layout" />
        <include android:id="@+id/layout_2" layout="@layout/included_layout" />
</LinearLayout>

The XML of the Included Layout:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/displayed_text"/>
</LinearLayout>

That's it!

Credit : Reference

Podgy answered 11/9, 2017 at 10:51 Comment(1)
This code is not tested, step 5 spelled "ButerKnife", missing a "t"Hester
B
0

Please visit Butterknife issue

The author @JakeWharton says -

Put an ID on the <include> and bind it to an arbitrary View field for each one. 
Then create an object which binds the inner views

static class Something {
  @Bind(R.id.something) TextView somethingView;
}

You can then create multiple instances of that object
and call ButterKnife.bind(something1, include1) and 
ButterKnife.bind(something2, include2) and so on.

Consider also just making a custom view instead of using <include> which has 
proper APIs to get its children (or at least binds them to fields itself).
Bertero answered 22/11, 2016 at 12:37 Comment(3)
I found this on github too. But did not understand it.Obligation
is include1 the id of the include tag?Obligation
not jst id, you need to pass view of that id i guessBertero
M
0

I Suggest you to use Data Binding Library provided by Google instead of Butterknife

Marchesa answered 22/11, 2016 at 12:49 Comment(2)
why databinding?at the moment it does not work with jackObligation
Jack doesn't work with anything. Although I agree, DBL is not needed to solve this issue.Pagination
D
0

Since this question is quite old.

I am able to connect to my id in include tag nowadays (2019/Oct).

Try to do something like the original question before you go deeply.

@BindView(R.id.your_button_id_inside_include_tag) Button button;
Deformity answered 10/10, 2019 at 10:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.