首页 > 解决方案 > RecyclerView onClick没有触发

问题描述

在过去的几个小时里,我一直在审查并尝试所有解决方案,但我无法让我的 RecyclerView 为我点击。我正在使用 MVVM,并使用列表向导为此生成了基本代码。我尝试使用新的匿名 View.OnClickListener 以及在 ViewHolder 类中实现 View.OnClickListener。我还尝试了一系列 clickable="true" [/false] 的组合,在片段中设置可点击...似乎没有任何效果。

最简单的示例是显示日历中的事件列表。以下是文件:

events_list_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.EventsListActivity">

    <fragment
        android:id="@+id/fragment"
        android:name="com.mycompany.myapp.ui.vf.EventsListFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</FrameLayout>

event_list_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/eventslist"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.vf.EventsListFragment">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/events_recycler"
        android:layout_width="395dp"
        android:layout_height="696dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:clickable="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/events_list_item"/>

</android.support.constraint.ConstraintLayout>

events_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:id="@+id/layout_event_list_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="50dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:orientation="horizontal"
        android:focusableInTouchMode="false"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent">

        <TextView
            android:id="@+id/txt_event_id"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textAlignment="textStart"
            android:contentDescription="@string/str_event_id"
            android:text="@string/str_id"
            android:focusableInTouchMode="false"
            />

        <TextView
            android:id="@+id/txt_event_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:contentDescription="@string/str_event_name"
            android:text="@string/str_event_name"
            android:textAlignment="textStart"
            android:focusableInTouchMode="false"
            tools:text="@string/str_event_name"
            />

        <TextView
            android:id="@+id/txt_event_desc"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:contentDescription="@string/str_event_desc"
            android:text="@string/str_event_desc"
            android:textAlignment="textStart"
            android:focusableInTouchMode="false"
            tools:text="@string/str_event_desc"
            />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_marginTop="8dp"
        android:layout_height="2dp"
        android:background="@color/list_line_divider"
        app:layout_constraintTop_toBottomOf="@id/layout_event_list_item"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />

</android.support.constraint.ConstraintLayout>

事件列表活动.java

public class EventsListActivity extends AppCompatActivity
    implements EventsListFragment.OnEventListFragmentInteractionListener
{

    private static final String TAG = "EventsListActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.events_list_activity);
        Bundle bundle = getIntent().getExtras();
        if (savedInstanceState == null) {
            EventsListFragment newFrag = EventsListFragment.newInstance();
            newFrag.setArguments(bundle);
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.container, newFrag)
                    .commitNow();
        }
    }

    @Override
    public void onListFragmentInteraction(Event item) {
        LogUtil.getInstance().Log(this, TAG, "ITEM NOT IMPLEMENTED");
        // click handler
        Intent intent = new Intent(this, EventInstanceActivity.class);
        intent.putExtra(Constants.ARG_EVENT, item);
        startActivity(intent);
    }
}

事件列表片段.java

public class EventsListFragment extends Fragment {
    private static final String TAG = "EventsListFragment";

    // TODO: Check that calendarId is going to be there
    // TODO: How to handle single vs. multiple EventInstances
    private Long mCalendarId;
    private EventViewModel mViewModel;
    private OnEventListFragmentInteractionListener mListener;

    public static EventsListFragment newInstance() {
        return new EventsListFragment();
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        // TODO: Check savedInstanceState
        Bundle bundle = getArguments();
        if (bundle != null) {
            mCalendarId = bundle.getLong(Constants.ARG_CALENDAR_ID);
        }
        return inflater.inflate(R.layout.events_list_fragment, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        LogUtil.getInstance().Log(this, TAG, "onActivityCreated");

        super.onActivityCreated(savedInstanceState);

        RecyclerView recyclerView = getActivity().findViewById(R.id.events_recycler);
        if (recyclerView != null) {
            Context context = recyclerView.getContext();
            mViewModel = ViewModelProviders.of(getActivity()).get(EventViewModel.class);
            mViewModel.setCalendar(mCalendarId);
            final EventRecyclerViewAdapter adapter =
                    new EventRecyclerViewAdapter(mViewModel.getEvents().getValue(), mListener);
            recyclerView.setLayoutManager(new LinearLayoutManager(context));
            recyclerView.setAdapter(adapter);

            mViewModel.getEvents().observe(this, new Observer<List<Event>>() {
                @Override
                public void onChanged(@Nullable List<Event> events) {
                    adapter.setEvents(events);
                    adapter.notifyDataSetChanged();
                }
            });
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    @Override
    public void onStart() {
        super.onStart();
        try {
            mListener = (OnEventListFragmentInteractionListener)getActivity();
        } catch (ClassCastException e) {
            throw new ClassCastException(getActivity().toString() + " must implement interface");
        }
    }

    public interface OnEventListFragmentInteractionListener {
        // TODO: Update argument type and name
        void onListFragmentInteraction(Event item);
    }
}

最后是 EventRecyclerViewAdapter.java

public class EventRecyclerViewAdapter extends RecyclerView.Adapter<EventRecyclerViewAdapter.ViewHolder> {

    // TODO: clean up naming standards 'm' for all class items
    List<Event> mEvents;
    EventsListFragment.OnEventListFragmentInteractionListener mListener;
    public EventRecyclerViewAdapter(List<Event> events, EventsListFragment.OnEventListFragmentInteractionListener listener) {
        mEvents = events;
        mListener = listener;
    }

    public void setEvents(List<Event> events) {
        mEvents = events;
    }

    @NonNull
    @Override
    public EventRecyclerViewAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.events_list_item, viewGroup, false);
        return new ViewHolder(view);
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(@NonNull EventRecyclerViewAdapter.ViewHolder viewHolder, int i) {
        final Event event = mEvents.get(i);
        viewHolder.mItem = event;
        viewHolder.mIdView.setText(event.getEventId().toString());
        viewHolder.mTitleView.setText(event.getTitle());
        viewHolder.mDescriptionView.setText(event.getDesc());

        viewHolder.mView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null) {
                    mListener.onListFragmentInteraction(event);
                }
            }
        });
    }

    @Override
    public int getItemCount() {
        return mEvents == null ? 0 : mEvents.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public final View mView;
        public final TextView mIdView;
        public final TextView mTitleView;
        public final TextView mDescriptionView;
        public Event mItem;

        public ViewHolder(View view) {
            super(view);
            mView = view;
            mIdView = view.findViewById(R.id.txt_event_id);
            mTitleView = view.findViewById(R.id.txt_event_name);
            mDescriptionView = view.findViewById(R.id.txt_event_desc);
        }

        @Override
        public String toString() {
            return super.toString() + " '" + mTitleView.getText() + "'";
        }
    }

}

可能是 xml 中的回收器视图在约束内吗?当我点击设备时,我可以听到系统声音,但它不会在实现侦听器的活动的断点处停止,也不会在 onClick 处理程序的断点处停止。有人能看出这里面少了什么吗?

TIA,迈克

标签: onclicklistenerandroid-recyclerviewrecyclerview-layout

解决方案


讨厌!在我发送这个之后,我发现我做错了什么。当它传递给适配器时,mListener 为空,因为我将它设置在onActivityCreated之后发生的 onStart 中。所以这是解决它的一种方法

    try {
        mListener = (OnEventListFragmentInteractionListener)getActivity();
    } catch (ClassCastException e) {
        throw new ClassCastException(getActivity().toString() + " must implement interface");
    }

这必须在 onAttach(context) 中完成,或者最迟在上面创建适配器的代码中完成。但是当我仔细观察这个时,我意识到片段所做的唯一事情就是传递它并存储对演员表的引用。我认为我们不需要存储它。所以我将适配器代码更改为:

final EventRecyclerViewAdapter adapter =
    new EventRecyclerViewAdapter(mViewModel.getEvents().getValue(),
        (OnListFragmentInteractionListener)getActivity());

由于活动实现了接口,只需在传递给适配器的过程中将其强制转换即可。您需要instanceof在执行此操作之前进行测试并酌情抛出。HTH,迈克


推荐阅读