2

I'm trying to write the below code without using the Butterknife method, mostly for learning purposes. Can anyone help with writing it without it's use please?

I'm guessing it's something along the lines of

TextView textName = findViewById(R.id.post_title); 

but it doesn't seem the run the same so I'm guessing I'm missing something

public class FriendsHolder extends RecyclerView.ViewHolder {
    @BindView(R.id.name)
    TextView textName;
    @BindView(R.id.image)
    CircleImageView imageView;
    @BindView(R.id.title)
    TextView textTitle;
    @BindView(R.id.company)
    TextView textCompany;

    public FriendsHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }
}
Ahmad
  • 69,608
  • 17
  • 111
  • 137
Mat
  • 23
  • 3

2 Answers2

5

Here is how you can do it when compile SDK version is greater than or equal to 26:

public class FriendsHolder extends RecyclerView.ViewHolder {
    TextView textName;
    CircleImageView imageView;
    TextView textTitle;
    TextView textCompany;

    public FriendsHolder(View itemView) {
        super(itemView);
        textName = itemView.findViewById(R.id.name);
        imageView = itemView.findViewById(R.id.image);
        textTitle = itemView.findViewById(R.id.title);
        textCompany = itemView.findViewById(R.id.company);
    }
}

For API versions less than 26 you just need to explicit typecast the view returned by findViewById method to the field type.

Abdul Wadood
  • 1,161
  • 1
  • 10
  • 18
  • 1
    I believe you are missing the explicit casts to the fields expected types. As it stands `findViewById` will only return a `View` object – Daniel W. Apr 30 '18 at 22:42
  • 1
    @DanielW. That was the old way. If you compile with the latest Android SDK version there is no need for explicit typecasting. You may refer to https://stackoverflow.com/questions/44902651/no-need-to-cast-the-result-of-findviewbyid?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Abdul Wadood Apr 30 '18 at 22:45
  • 1
    Awesome! Didn't notice they improved this. Have been using Kotlin for so long :P ...there only seems to be a small caveat. From the answer you linked: "So if your compileSdk is at least 26, it means that you can make use of this :)". Maybe mention that limitation in your answer? Just in case someone trips over it – Daniel W. Apr 30 '18 at 22:50
  • `findViewById()` supports generics now: https://developer.android.com/reference/android/view/View.html#findViewById(int) – Ahmad Apr 30 '18 at 22:51
  • 1
    @DanielW. Now I have added that. Thanks. – Abdul Wadood Apr 30 '18 at 23:07
3

As you probably know, ButterKnife is an annotation processor that takes your code at compile time, evaluates the annotations (eg. @BindView) and generates code to replace them with.

Now in the case of @BindView what the annotation processor actually does is:

  1. find the desired view by its ID

and

  1. cast it to the expected type.

So a functional equivalent of your example would be something like this:

public class FriendsHolder extends RecyclerView.ViewHolder {

    private TextView textName;
    private CircleImageView imageView;
    private TextView textTitle;
    private TextView textCompany;

    FriendsHolder(View itemView) {
        super(itemView);
        bindViews(itemView);
    }

    private void bindViews(View root) {
        textName = (TextView) root.findViewById(R.id.name);
        imageView = (CircleImageView) root.findViewById(R.id.image);
        textTitle = (TextView) root.findViewById(R.id.title);
        textCompany = (TextView) root.findViewById(R.id.company);
    }
}

You can read more about the underlying processes in this blog post or have a look at some of the ButterKnife source code.

Where you can see that essentially what they do is look for the ID on the source view and try to cast it to the expected type that was read from the annotated field.

Excerpt from butterknife.internal.Utils:

public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who, Class<T> cls) {
    View view = findRequiredView(source, id, who);
    return castView(view, id, who, cls);
}

public static View findRequiredView(View source, @IdRes int id, String who) {
    View view = source.findViewById(id);
    if (view != null) {
      return view;
    }
    // If view is null throw IllegalStateException
}

public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
    try {
      return cls.cast(view); // <-- casting 
    } catch (ClassCastException e) {
        throw new IllegalStateException
        // Exception handling...
    }
Daniel W.
  • 623
  • 6
  • 14