TechSpellBound
TechSpellBound

Reputation: 2555

Injecting view in a listener not working with Butterknife

To avoid "inner class hell" in case of Android event listeners, I have moved the listeners to separate classes. Following is one of such listeners for a TextView which holds a date string. On touching it, I open a DatePickerDialog and set the selected date value back to the TextView.

I further enhanced this listener to use Butterknife as follows:

public class DateViewClickListener implements View.OnClickListener {

    private final DateTime prevDate;
    private DateTimeFormatter dateFmt;

    public DateViewClickListener(DateTime prevDate, DateTimeFormatter dateFmt) {
        this.prevDate = prevDate;
        this.dateFmt = dateFmt;
    }

    @Override
    public void onClick(View view) {
        new DatePickerDialog(view.getContext(), new DatePickerDialog.OnDateSetListener() {

            @InjectView(R.id.date)
            TextView dateView;

            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                Activity activity = ActivityUtil.getParentActivity(view);
                ButterKnife.inject(this, activity.getWindow().getDecorView());
                DateTime newDate = prevDate.withDate(year, monthOfYear, dayOfMonth);
                dateView.setText(dateFmt.print(newDate));
            }
        }, prevDate.getYear(), prevDate.getMonthOfYear(), prevDate.getDayOfMonth()).show();
    }
}

ActivityUtil.getParentActivity(view) used in the above code, scans through the context hierarchy of the view and finds its parent activity. Following is the code for it:

public class ActivityUtil {

    public static Activity getParentActivity(View view) {
        Context context = view.getContext();
        return scanForActivity(context);
    }

    private static Activity scanForActivity(Context context) {
        if (context == null)
            return null;
        else if (context instanceof Activity)
            return (Activity) context;
        else if (context instanceof ContextWrapper)
            return scanForActivity(((ContextWrapper) context).getBaseContext());
        return null;
    }
}

On executing this code, the dateView remains null throwing an NPE. However, this code works for me when I don't use Butterknife (the DateViewClickListener class is as follows in that case).

public class DateViewClickListener implements View.OnClickListener {

    private final DateTime prevDate;
    private DateTimeFormatter dateFmt;

    public DateViewClickListener(DateTime prevDate, DateTimeFormatter dateFmt) {
        this.prevDate = prevDate;
        this.dateFmt = dateFmt;
    }

    @Override
    public void onClick(View view) {
        new DatePickerDialog(view.getContext(), new DatePickerDialog.OnDateSetListener() {

            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                Activity activity = ActivityUtil.getParentActivity(view);
                TextView dateView = (TextView) activity.findViewById(R.id.date);
                DateTime newDate = prevDate.withDate(year, monthOfYear, dayOfMonth);
                dateView.setText(dateFmt.print(newDate));
            }
        }, prevDate.getYear(), prevDate.getMonthOfYear(), prevDate.getDayOfMonth()).show();
    }
}

Where is my understanding going wrong?

Upvotes: 0

Views: 514

Answers (1)

TechSpellBound
TechSpellBound

Reputation: 2555

It seems that Butterknife injection works only if Butterknife.inject() statement is present in the constructor or any of the Android life-cycle methods.

Hence, I have modified the DateViewClickListener class to accept the activity that it gets called from and use it as the "source" for Butterknife injection something like:

public class DateViewClickListener implements View.OnClickListener {

    @InjectView(R.id.date)
    TextView dateView;


    private final DateTime prevDate;
    private DateTimeFormatter dateFmt;

    public DateViewClickListener(DateTime prevDate, DateTimeFormatter dateFmt, Activity contextActivity) {
        Butterknife.inject(this, contextActivity);
        this.prevDate = prevDate;
        this.dateFmt = dateFmt;
    }

    .....
}

And then instantiate the listener like:

public class MainActivity extends ActionBarActivity {

    .....

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......

        date.setOnClickListener(new DateViewClickListener(paymentDate, dateFmt, this));

        ......

    }

    .....

}

Upvotes: 1

Related Questions