【问题标题】:FirebaseRecyclerAdapter : pass Activity Context to ViewHolderFirebaseRecyclerAdapter :将 Activity 上下文传递给 ViewHolder
【发布时间】:2016-11-20 14:36:23
【问题描述】:

列表项中的每个操作都是从RecyclerView.ViewHolder 处理的。实际上,我想将 Activity 上下文传递给FirebaseRecyclerAdapter 中的 ViewHolder,所以我尝试将 Constructor 添加到此 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) 在 android.support.v7.widget.RecyclerView$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.layoutHorizo​​ntal(LinearLayout.java:1732) 在 android.widget.LinearLayout.onLayout(LinearLayout.java:1497) 在 android.view.View.layout(View.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) 在 android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1319) 在 android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:815) 在 android.view.View.layout(View.java:16651) 在 android.view.ViewGroup.layout(ViewGroup.java:5440) 在 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(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) 在 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(View.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

【问题讨论】:

  • 返回构造函数.newInstance(mContext, view);尝试返回 constructor.newInstance((Activity)mContext, view);
  • @VishalPatoliya 我认为我们不需要任何演员表。

标签: android firebase firebase-realtime-database firebaseui


【解决方案1】:

您的 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) 构造函数可能是一个很好的补充。您可以向 FirebaseUI github 存储库添加功能请求吗?

更新:feature request on Github 适合那些想要 +1 的人。 :-)

【讨论】:

  • 好的,先生。我很乐意这样做.. :)
【解决方案2】:

将 Activity 上下文传递给 Adapter 并不总是正确的做法。您应该创建一个您的 Activity 实现的接口并将其传递给您的适配器。 另一种流行的方法是使用 pubsub 模式来引发事件并在 EventBus 等库发生某些事情时订阅它们。

【讨论】:

    【解决方案3】:

    解决方案:

    我创建了一个接口OnTodoActionListener.java如下:

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

    然后,我在Activity中创建了这个接口的对象为:

    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 中创建了一个setter 方法,并使用了它的引用如下:

    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);
    }
    

    最后,我将在 Activity 中创建的监听器设置为:

    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);
    

    最后,我现在可以使用 Firebase 实时数据库创建一个完整的“Todo App”演示。上传后,我会将链接发布到 Github。谢谢。

    更新:您可以使用 Firebase 数据库创建我的 Todo App 演示。这是链接:Firebase Database Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-09
      • 2011-07-24
      • 2017-01-25
      • 2020-06-25
      • 1970-01-01
      • 2018-03-05
      • 1970-01-01
      • 2014-03-13
      相关资源
      最近更新 更多