FirebaseRecyclerAdapter:将活动上下文传递给ViewHolder

时间:2016-07-18 05:19:26

标签: android firebase firebase-realtime-database firebaseui

列表项中的每个操作都是从RecyclerView.ViewHolder处理的。实际上,我想在FirebaseRecyclerAdapter中将Activity上下文传递给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

3 个答案:

答案 0 :(得分:3)

您的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 repo添加功能请求吗?

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

解决方案:

我创建了一个界面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);

最后,我现在能够创建一个完整工作的演示&#34; Todo App&#34;使用Firebase实时数据库。我上传后会将链接发布到Github。感谢。

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