Reputation: 958
I started to refactor my application to use the Butterknife library for view binding. Everything works as expected in my activity class, but i'm having trouble understanding how to use butterknife appropriately in my static fragment class. My fragment contains a RecyclerView
adapter containing a list of CardView
elements.
Before Refactor
public static class PlaceholderFragment extends Fragment {
Activity mActivity;
RecyclerView mRecyclerView;
TaskAdapter taskAdapter;
FloatingActionButton mFabView;
TextView m_ID;
TextView mTitle;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.cardList);
mFabView = (FloatingActionButton) rootView.findViewById(R.id.normal_plus);
mFabView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
and also
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView.setAdapter(taskAdapter);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
taskAdapter.SetOnItemClickListener(new TaskAdapter.OnItemClickListener() {
@Override
public void onItemClick(View v, int position) {
m_ID = (TextView) v.findViewById(R.id._task_id);
mTitle = (TextView) v.findViewById(R.id.title);
Intent detailIntent = new Intent(v.getContext(), DetailActivity.class);
detailIntent.putExtra("TASK_ID", m_ID.getText().toString());
detailIntent.putExtra("TASK_TITLE", mTitle.getText().toString());
After Refactor
public static class PlaceholderFragment extends Fragment {
Activity mActivity;
TaskAdapter taskAdapter;
@Bind(R.id.cardList) RecyclerView mRecyclerView;
@Bind(R.id.normal_plus) FloatingActionButton mFabView;
@Bind(R.id._task_id) TextView m_ID;
@Bind(R.id.title) TextView mTitle;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
ButterKnife.bind(this, rootView);
...
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView.setAdapter(taskAdapter);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
taskAdapter.SetOnItemClickListener(new TaskAdapter.OnItemClickListener() {
@Override
@OnClick({R.id._task_id, R.id.title})
public void onItemClick(View v, int position) {
ButterKnife.bind(this, v);
Intent detailIntent = new Intent(v.getContext(), DetailActivity.class);
detailIntent.putExtra("TASK_ID", m_ID.getText().toString());
detailIntent.putExtra("TASK_TITLE", mTitle.getText().toString());
The exception i'm seeing is
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.github.idclark.forgetmenot/com.github.idclark.forgetmenot.MainActivity}: java.lang.RuntimeException: Unable to bind views for com.github.idclark.forgetmenot.MainActivity$PlaceholderFragment
...
Caused by: java.lang.RuntimeException: Unable to bind views for com.github.idclark.forgetmenot.MainActivity$PlaceholderFragment
at butterknife.ButterKnife.bind(ButterKnife.java:322)
at butterknife.ButterKnife.bind(ButterKnife.java:279)
at com.github.idclark.forgetmenot.MainActivity$PlaceholderFragment.onCreateView(MainActivity.java:86)
from the stacktrace it looks like that because i'm now effectively trying to bind both m_ID
and mTitle
in onCreateView()
instead of the onItemClick()
callback and that's why the views are null. Is that correct? I'm also calling Butterknife.bind()
twice, once in onCreteView
for the mRecyclerView
and mFabView
, but Butterknife.bind()
is also called in the callback. How can bind the m_ID
and mTitle
views at the appropriate time / what method beside onCreateView
should I use?
Upvotes: 1
Views: 444
Reputation: 16010
You should ButterKnife.bind(...)
only once, in onCreateView()
, just as you did.
For callback, you should cast you View
to actual custom View
class (which is also binding it's widgets!), and get title
and id
from there:
public class MyCustomView extends FrameLayout {
public @Bind(R.id._task_id) TextView m_ID;
public @Bind(R.id.title) TextView mTitle;
public MyCustomView(Context context) {
super(context);
initialize(context);
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
void initialize(Context context) {
LayoutInflater.from(context).inflate(R.layout.my_custom_view, this);
ButterKnife.bind(this);
}
}
And then you have to
Remove TextView
's references from PlaceholderFragment
TextView m_ID;
TextView mTitle;
Fix handler:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView.setAdapter(taskAdapter);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
taskAdapter.SetOnItemClickListener(new TaskAdapter.OnItemClickListener() {
@Override
@OnClick({R.id._task_id, R.id.title})
public void onItemClick(View v, int position) {
MyCustomView myView = (MyCustomView)v;
Intent detailIntent = new Intent(v.getContext(), DetailActivity.class);
detailIntent.putExtra("TASK_ID", myView .m_ID.getText().toString());
detailIntent.putExtra("TASK_TITLE", myView .mTitle.getText().toString());
I hope, it helps
Upvotes: 1