2016-07-18 4 views
3

Каждое действие в элементе списка обрабатывается от RecyclerView.ViewHolder На самом деле, я хочу, чтобы передать контекст активность в ViewHolder в FirebaseRecyclerAdapter, так что я пытался добавить конструктор к этому ViewHolder, как показано ниже:FirebaseRecyclerAdapter: передача активность Контекст для ViewHolder

public class TodoViewHolder extends RecyclerView.ViewHolder { 

    @BindView(R.id.accbListItemHome) 
    AppCompatCheckBox mAppCompatCheckBox; 
    @BindView(R.id.actvListItemHome) 
    AppCompatTextView mAppCompatTextView; 
    @BindView(R.id.acibListItemHome) 
    AppCompatImageButton mAppCompatImageButton; 
    private Context mContext; 

    public TodoViewHolder(Context context, View itemView) { 
     super(itemView); 
     mContext = context; 
     ButterKnife.bind(this, itemView); 
    } 

    public AppCompatCheckBox getmAppCompatCheckBox() { 
     return mAppCompatCheckBox; 
    } 

    public AppCompatTextView getmAppCompatTextView() { 
     return mAppCompatTextView; 
    } 

    public AppCompatImageButton getmAppCompatImageButton() { 
     return mAppCompatImageButton; 
    } 

    @OnCheckedChanged(R.id.accbListItemHome) 
    void onCheckBoxChange(CompoundButton compoundButton, boolean checked) { 
     TodoMasterModel todoMasterModel = (TodoMasterModel) compoundButton.getTag(); 
     todoMasterModel.getmTodoModel().setmIsCompleted(checked); 
     ((HomeActivity) mContext).onDoneClick(todoMasterModel, AddEditDialogFragment.ACTION_Edit); 
     Log.i("Checkbox", todoMasterModel.toString()); 
    } 

    @OnClick(R.id.actvListItemHome) 
    void onTextViewClick(View view) { 
     TodoMasterModel todoMasterModel = (TodoMasterModel) view.getTag(); 
     ((HomeActivity) mContext).showAddEditDialog(todoMasterModel, AddEditDialogFragment.ACTION_Edit); 
     Log.i("TextView", todoMasterModel.toString()); 
    } 

    @OnClick(R.id.acibListItemHome) 
    void onImageButtonClick(View view) { 
     TodoMasterModel todoMasterModel = (TodoMasterModel) view.getTag(); 
     FirebaseDatabase.getInstance().getReference("todos").child(todoMasterModel.getmId()).setValue(todoMasterModel.getmTodoModel()); 
     Log.i("Delete", todoMasterModel.toString()); 
    } 
} 

Я создал класс и модифицированный FirebaseRecyclerAdapter для моей цели, как показано ниже:

public abstract class MyFirebaseAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { 

    private Context mContext; 
    protected int mModelLayout; 
    Class<T> mModelClass; 
    Class<VH> mViewHolderClass; 
    FirebaseArray mSnapshots; 

    /** 
    * @param modelClass  Firebase will marshall the data at a location into an instance of a class that you provide 
    * @param modelLayout  This is the layout used to represent a single item in the list. You will be responsible for populating an 
    *      instance of the corresponding view with the data from an instance of modelClass. 
    * @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. 
    * @param ref    The Firebase location to watch for data changes. Can also be a slice of a location, using some 
    *      combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code> 
    */ 
    public MyFirebaseAdapter(Context context, Class<T> modelClass, int modelLayout, Class<VH> viewHolderClass, Query ref) { 
     mContext = context; 
     mModelClass = modelClass; 
     mModelLayout = modelLayout; 
     mViewHolderClass = viewHolderClass; 
     mSnapshots = new FirebaseArray(ref); 

     mSnapshots.setOnChangedListener(new FirebaseArray.OnChangedListener() { 
      @Override 
      public void onChanged(EventType type, int index, int oldIndex) { 
       switch (type) { 
        case Added: 
         notifyItemInserted(index); 
         break; 
        case Changed: 
         notifyItemChanged(index); 
         break; 
        case Removed: 
         notifyItemRemoved(index); 
         break; 
        case Moved: 
         notifyItemMoved(oldIndex, index); 
         break; 
        default: 
         throw new IllegalStateException("Incomplete case statement"); 
       } 
      } 
     }); 
    } 

    /** 
    * @param modelClass  Firebase will marshall the data at a location into an instance of a class that you provide 
    * @param modelLayout  This is the layout used to represent a single item in the list. You will be responsible for populating an 
    *      instance of the corresponding view with the data from an instance of modelClass. 
    * @param viewHolderClass The class that hold references to all sub-views in an instance modelLayout. 
    * @param ref    The Firebase location to watch for data changes. Can also be a slice of a location, using some 
    *      combination of <code>limit()</code>, <code>startAt()</code>, and <code>endAt()</code> 
    */ 
    public MyFirebaseAdapter(Context context, Class<T> modelClass, int modelLayout, Class<VH> viewHolderClass, DatabaseReference ref) { 
     this(context, modelClass, modelLayout, viewHolderClass, (Query) ref); 
    } 

    public void cleanup() { 
     mSnapshots.cleanup(); 
    } 

    @Override 
    public int getItemCount() { 
     return mSnapshots.getCount(); 
    } 

    public T getItem(int position) { 
     return parseSnapshot(mSnapshots.getItem(position)); 
    } 

    /** 
    * This method parses the DataSnapshot into the requested type. You can override it in subclasses 
    * to do custom parsing. 
    * 
    * @param snapshot the DataSnapshot to extract the model from 
    * @return the model extracted from the DataSnapshot 
    */ 
    protected T parseSnapshot(DataSnapshot snapshot) { 
     return snapshot.getValue(mModelClass); 
    } 

    public DatabaseReference getRef(int position) { 
     return mSnapshots.getItem(position).getRef(); 
    } 

    @Override 
    public long getItemId(int position) { 
     // http://stackoverflow.com/questions/5100071/whats-the-purpose-of-item-ids-in-android-listview-adapter 
     return mSnapshots.getItem(position).getKey().hashCode(); 
    } 

    @Override 
    public VH onCreateViewHolder(ViewGroup parent, int viewType) { 
     ViewGroup view = (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); 
     try { 
      Constructor<VH> constructor = mViewHolderClass.getConstructor(View.class); 
      return constructor.newInstance(mContext, view); 
     } catch (NoSuchMethodException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } catch (InstantiationException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public void onBindViewHolder(VH viewHolder, int position) { 
     T model = getItem(position); 
     populateViewHolder(viewHolder, model, position); 
    } 

    @Override 
    public int getItemViewType(int position) { 
     return mModelLayout; 
    } 

    /** 
    * Each time the data at the given Firebase location changes, this method will be called for each item that needs 
    * to be displayed. The first two arguments correspond to the mLayout and mModelClass given to the constructor of 
    * this class. The third argument is the item's position in the list. 
    * <p> 
    * Your implementation should populate the view using the data contained in the model. 
    * 
    * @param viewHolder The view to populate 
    * @param model  The object containing the data used to populate the view 
    * @param position The position in the list of the view being populated 
    */ 
    abstract protected void populateViewHolder(VH viewHolder, T model, int position); 
} 

Ошибка приходит из-за этой линии:

return constructor.newInstance(mContext, view); 

Но я получаю исключение NoSuchMethod. Я знал, что делаю это неправильно. Но я не собираюсь это делать. Может ли кто-нибудь помочь мне с этим?

java.lang.RuntimeException: java.lang.NoSuchMethodException: [класс android.view.View] на com.letsnurture.android.firebasedatabasedemo.adapter.MyFirebaseAdapter.onCreateViewHolder (MyFirebaseAdapter.java:120) на android.support.v7.widget.RecyclerView $ Adapter.createViewHolder (RecyclerView.java:5779) в android.support.v7.widget.RecyclerView $ Recycler.getViewForPosition (RecyclerView.java:5003) на андроида .support.v7.widget.RecyclerVie ж $ Recycler.getViewForPosition (RecyclerView.java:4913) в android.support.v7.widget.LinearLayoutManager $ LayoutState.next (LinearLayoutManager.java:2029) в android.support.v7.widget.LinearLayoutManager.layoutChunk (LinearLayoutManager.java:1414) на android.support.v7.widget.LinearLayoutManager.fill (LinearLayoutManager.java:1377) на android.support.v7.widget.LinearLayoutManager.onLayoutChildren (LinearLayoutManager.java:578) на android.support.v7.widget.RecyclerView.dispatchLayoutStep2 (RecyclerView.java:3260) на android.support.v7.widget.RecyclerView.dispatchLayout (RecyclerView.java:3069) на android.support.v7.widget.RecyclerView.onLayout (RecyclerView.java:3518) в android.view.View.layout (View.java:16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.widget.LinearLayout.setChildFrame (LinearLayout.java:1743) на android.widget.LinearLayout.layoutHorizontal (LinearLayout.java:1732) на android.widget.LinearLayout.onLayout (LinearLayout.java:1497) на android.view.View.layout (вид. Java: 16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.support.design.widget.HeaderScrollingViewBehavior.layoutChild (HeaderScrollingViewBehavior.java:120) на android.support.design. widget.ViewOffsetBehavior.onLayoutChild (ViewOffsetBehavior.java:42) at android.support.design.widget.AppBarLayout $ ScrollingViewBehavior.onLayoutChild (AppBarLayout.Java: 1 319) на android.support.design.widget.CoordinatorLayout.onLayout (CoordinatorLayout.java:815) на android.view.View.layout (View.java:16651) в android.view.ViewGroup.layout (ViewGroup.java:5440) at android.support.v4.widget.DrawerLayout.onLayout (DrawerLayout.java:1191) на android.view.View.layout (View.java:16651) на android.view. ViewGroup.layout (ViewGroup.java:5440) на android.widget.FrameLayout.layoutChildren (FrameLayout.java:336) на android.widget.FrameLayout.onLayout (Fr ameLayout.java:273) на android.view.View.layout (View.java:16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.widget.LinearLayout.setChildFrame (LinearLayout. java: 1743) на android.widget.LinearLayout.layoutVertical (LinearLayout.java:1586) на android.widget.LinearLayout.onLayout (LinearLayout.java:1495) на android.view.View.layout (View.java: 16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.widget.FrameLayout.layoutChildren (FrameLayout.java:336) на android.widget.FrameLayout.onLayout (FrameLayout.java:273) на android.view.View.layout (View.java:16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android .widget.LinearLayout.setChildFrame (LinearLayout.java:1743) на android.widget.LinearLayout.layoutVertical (LinearLayout.java:1586) на android.widget.LinearLayout.onLayout (LinearLayout.java:1495) в android.view .View.layout (View.java:16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.widget.FrameLayout.layoutChildren (FrameLayout.java:336) на android.widget.FrameLayout.onLayout (FrameLayout.java:273) на com.android.internal.policy.PhoneWindow $ DecorView.onLayout (PhoneWindow.java:2678) на android.view.View.layout (Просмотреть .java: 16651) на android.view.ViewGroup.layout (ViewGroup.java:5440) на android.view.ViewRootImpl.performLayout (ViewRootImpl.java:2183) на android.view.ViewRootImpl.performTraversals (ViewRootImpl.java : 1943) на android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:1119) на android.view.ViewRootImpl $ TraversalRunnable .run (ViewRootImpl.java:6060) на android.view.Choreographer $ CallbackRecord.run (Choreographer.java:858) в android.vi

+0

return constructor.newInstance (mContext, view); try return constructor.newInstance ((Activity) mContext, view); –

+0

@ VishalPatoliya Я не думаю, что нам нужно какое-то литье. –

+0

так, попробуйте себя –

ответ

0

Решение:

Я создал интерфейс OnTodoActionListener.java, как показано ниже:

public interface OnTodoActionListener { 
    void onCheckBoxChange(CompoundButton compoundButton, boolean checked); 
    void onTextViewClick(View view); 
    void onImageButtonClick(View view); 
} 

Затем я создал объект этого интерфейса в деятельности, как:

private OnTodoActionListener mOnTodoActionListener; 

mOnTodoActionListener = new OnTodoActionListener() { 
     @Override 
     public void onCheckBoxChange(CompoundButton compoundButton, boolean checked) { 
      TodoMasterModel todoMasterModel = (TodoMasterModel) compoundButton.getTag(); 
      todoMasterModel.getmTodoModel().setmIsCompleted(checked); 
      onDoneClick(todoMasterModel, AddEditDialogFragment.ACTION_Edit); 
      Log.i("Checkbox", todoMasterModel.toString()); 
     } 

     @Override 
     public void onTextViewClick(View view) { 
      TodoMasterModel todoMasterModel = (TodoMasterModel) view.getTag(); 
      showAddEditDialog(todoMasterModel, AddEditDialogFragment.ACTION_Edit); 
      Log.i("TextView", todoMasterModel.toString()); 
     } 

     @Override 
     public void onImageButtonClick(View view) { 
      TodoMasterModel todoMasterModel = (TodoMasterModel) view.getTag(); 
      mDatabaseReferenceTodo.child(todoMasterModel.getmId()).setValue(null); 
      Log.i("Delete", todoMasterModel.toString()); 
     } 
    }; 

Я сделал метод установки в моем TodoViewHolder и использовал его ссылку, как указано ниже:

private OnTodoActionListener mOnTodoActionListener; 

public void setListener(OnTodoActionListener onTodoActionListener) { 
    mOnTodoActionListener = onTodoActionListener; 
} 

@OnCheckedChanged(R.id.accbListItemHome) 
void onCheckBoxChange(CompoundButton compoundButton, boolean checked) { 
    mOnTodoActionListener.onCheckBoxChange(compoundButton, checked); 
} 

@OnClick(R.id.actvListItemHome) 
void onTextViewClick(View view) { 
    mOnTodoActionListener.onTextViewClick(view); 
} 

@OnClick(R.id.acibListItemHome) 
void onImageButtonClick(View view) { 
    mOnTodoActionListener.onImageButtonClick(view); 
} 

И, наконец, я поставил слушателя, созданного в деятельности, как:

FirebaseRecyclerAdapter mHomeRecyclerAdapter = new FirebaseRecyclerAdapter<TodoModel, TodoViewHolder>(TodoModel.class, R.layout.list_item_home, TodoViewHolder.class, mDatabaseReferenceTodo) { 
     @Override 
     public void populateViewHolder(TodoViewHolder todoViewHolder, TodoModel todoModel, int position) { 

      todoViewHolder.setListener(mOnTodoActionListener); 

      TodoMasterModel todoMasterModel = new TodoMasterModel(getRef(position).getKey(), todoModel); 

      todoViewHolder.getmAppCompatCheckBox().setTag(todoMasterModel); 
      todoViewHolder.getmAppCompatTextView().setTag(todoMasterModel); 
      todoViewHolder.getmAppCompatImageButton().setTag(todoMasterModel); 

      todoViewHolder.getmAppCompatCheckBox().setChecked(todoModel.ismIsCompleted()); 
      todoViewHolder.getmAppCompatTextView().setText(todoModel.getmTodoContent()); 

     } 
    }; 
    mRecyclerView.setAdapter(mHomeRecyclerAdapter); 

И, наконец, я теперь в состоянии создать полностью рабочую демо «Todo App» с помощью Firebase в реальном времени базы данных. Я отправлю ссылку на Github после ее загрузки. Благодарю.

Обновление: Возможно, у вас есть демо-приложение Todo, созданное с использованием базы данных firebase. Вот ссылка: Firebase Database Demo

2

Ваш ViewHolder подклассу необходим конструктор только с параметром View , Из FirebaseUI documentation:

public static class ChatHolder extends RecyclerView.ViewHolder { 
    View mView; 

    public ChatHolder(View itemView) { 
     super(itemView); 
     mView = itemView; 
    } 

Причина заключается в this code, который ищет только одной подписи. Добавление кода для поиска конструктора ViewHolder(Context, View) может быть хорошим дополнением.Можете ли вы добавить запрос функции в репозиторий gitub для FirebaseUI?

Обновление: feature request on Github для тех, кто ищет +1 его. :-)

+0

Уверенный сэр. Я бы с удовольствием это сделал .. :) –

0

Передача контекста активности адаптера - это не всегда правильный способ сделать что-то. Вам лучше создать интерфейс, который ваша деятельность реализует и передает его вашему адаптеру. Другим популярным способом является использование шаблона pubsub для создания событий и подписки на них, когда что-то происходит с библиотеками, такими как EventBus.