【问题标题】:Android: Sending a cardview from one fragment to anotherAndroid:将卡片视图从一个片段发送到另一个片段
【发布时间】:2020-06-07 07:28:27
【问题描述】:

我需要一些暑期项目的帮助

This is my Events fragment

This is my MyList fragment

我正在使用 RecyclerView+Cardview 来显示事件。这个想法是用户可以点击每张卡片右侧的大加号,卡片将显示在 MyList 片段中。我想问是否可以将卡片直接从一个片段转移到另一个片段?此外,两个片段都包含在同一个活动中,这使得它有点棘手(我还没有找到任何可用的解决方案)。

如果这不可行,另一种方法是将 CardView 中包含的引用类型对象传输到 MyList 片段。然而,这更不简单。这是因为按钮在适配器中被充气,但是这里没有创建引用类型对象。我看过很多关于使用 Parcelable 接口的教程,但是,当我什至无法在适配器中创建对象时,我不知道如何在这里实现它。引用对象在另一个活动中创建,并在读取和显示之前存储在 Firebase 中。

我将在下面附上我的 EventsAdapter.java 和 EventsItem.java 和 EventsFragment.java 代码,但如果我应该包含更多代码来描述问题,请告诉我。

感谢您阅读我很长的帖子!


public class EventsAdapter extends RecyclerView.Adapter<EventsAdapter.EventsViewHolder> implements Filterable {
    private ArrayList<EventsItem> mEventsList;
    private ArrayList<EventsItem> mEventsListFull;
    private EventsAdapter.OnItemClickListener mListener;
    private Context mContext;
    private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.UK);

    public interface OnItemClickListener {
        void onItemClick(int position);
    }


    //the ViewHolder holds the content of the card
    public static class EventsViewHolder extends RecyclerView.ViewHolder {

        public ImageView mImageView;
        public ImageView mAddButton;
        public TextView mTextView1;
        public TextView mTextView2;
        public TextView mTextView3;
        public TextView mTextView4;
        public TextView mTextView5;

        public EventsViewHolder(Context context, View itemView, final EventsAdapter.OnItemClickListener listener) {
            super(itemView);
            final Context context1 = context;
            mImageView = itemView.findViewById(R.id.imageView);
            mAddButton = itemView.findViewById(R.id.image_add);
            mTextView1 = itemView.findViewById(R.id.title);
            mTextView2 = itemView.findViewById(R.id.event_description);
            mTextView3 = itemView.findViewById(R.id.date);
            mTextView4 = itemView.findViewById(R.id.location);
            mTextView5 = itemView.findViewById(R.id.time);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (listener != null) {
                        int position = getAdapterPosition();
                        if (position != RecyclerView.NO_POSITION) {
                            listener.onItemClick(position);
                        }
                    }
                }
            });
            mAddButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String str1 = mTextView1.getText().toString();
                    String str2 = mTextView2.getText().toString();
                    String str3 = mTextView3.getText().toString();
                    String str4 = mTextView4.getText().toString();
                    String str5 = mTextView5.getText().toString();

                    Bundle bundle = new Bundle();
                    bundle.putString("title", str1);
                    bundle.putString("event description", str2);
                    bundle.putString("date", str3);
                    bundle.putString("location", str4);
                    bundle.putString("time", str5);
                    MylistFragment mlf = new MylistFragment();
                    mlf.setArguments(bundle);
                }
            });
        }

    }

    //Constructor for EventsAdapter class. This ArrayList contains the
    //complete list of items that we want to add to the View.
    public EventsAdapter(Context context, ArrayList<EventsItem> EventsList) {
        mEventsList = EventsList;
        mContext = context;
        mEventsListFull = new ArrayList<>(EventsList); // copy of EventsList for SearchView
    }

    //inflate the items in a EventsViewHolder
    @NonNull
    @Override
    public EventsAdapter.EventsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.event_item, parent, false);
        EventsAdapter.EventsViewHolder evh = new EventsAdapter.EventsViewHolder(mContext, v, mListener);
        return evh;
    }

    @Override
    public void onBindViewHolder(@NonNull EventsAdapter.EventsViewHolder holder, int position) {
        EventsItem currentItem = mEventsList.get(position);
        holder.mImageView.setImageResource(currentItem.getProfilePicture());
        holder.mTextView1.setText(currentItem.getTitle());
        holder.mTextView2.setText(currentItem.getDescription());
        holder.mTextView3.setText(df.format(currentItem.getDateInfo()));
        holder.mTextView4.setText(currentItem.getLocationInfo());
        holder.mTextView5.setText(currentItem.getTimeInfo());
    }



    @Override
    public int getItemCount() {
        return mEventsList.size();
    }
public class EventsItem implements Occasion, Parcelable {
    //fields removed for brevity

    //constructor removed for brevity
    }

    public EventsItem() {

    }

    public EventsItem(Parcel in) {
        profilePicture = in.readInt();
        timeInfo = in.readString();
        hourOfDay = in.readInt();
        minute = in.readInt();
        locationInfo = in.readString();
        title = in.readString();
        description = in.readString();
    }

    public static final Creator<EventsItem> CREATOR = new Creator<EventsItem>() {
        @Override
        public EventsItem createFromParcel(Parcel in) {
            return new EventsItem(in);
        }

        @Override
        public EventsItem[] newArray(int size) {
            return new EventsItem[size];
        }
    };

    //getter methods have been removed for brevity
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(profilePicture);
        dest.writeString(timeInfo);
        dest.writeString(locationInfo);
        dest.writeString(title);
        dest.writeString(description);
        dest.writeString(df.format(dateInfo));
        dest.writeInt(hourOfDay);
        dest.writeInt(minute);
    }
}
public class EventsFragment extends Fragment {
    ArrayList<EventsItem> EventsItemList;
    FirebaseDatabase mDatabase;
    DatabaseReference mDatabaseReference;
    ValueEventListener mValueEventListener;
    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private EventsAdapter mAdapter;
    private View rootView;
    public FloatingActionButton floatingActionButton;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_events, container, false);
        mDatabase = FirebaseDatabase.getInstance();
        mDatabaseReference = mDatabase.getReference().child("Events");

        createEventsList();
        buildRecyclerView();

        floatingActionButton = rootView.findViewById(R.id.fab);
        floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), EventsAdder.class);
                startActivity(intent);
            }
        });
        mValueEventListener = new ValueEventListener() {

            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                    EventsItemList.add(snapshot.getValue(EventsItem.class));
                }
                EventsAdapter eventsAdapter = new EventsAdapter(getActivity(), EventsItemList);
                mRecyclerView.setAdapter(eventsAdapter);
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        };
        mDatabaseReference.addValueEventListener(mValueEventListener);

        setHasOptionsMenu(true);
        Toolbar toolbar = rootView.findViewById(R.id.events_toolbar);
        AppCompatActivity activity = (AppCompatActivity) getActivity();
        activity.setSupportActionBar(toolbar);
        return rootView;
    }

    public void createEventsList() {
        EventsItemList = new ArrayList<>();
    }

    public void buildRecyclerView() {
        mRecyclerView = rootView.findViewById(R.id.recyclerview);
        mLayoutManager = new LinearLayoutManager(getContext());
        mAdapter = new EventsAdapter(getActivity(), EventsItemList);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    }

}

【问题讨论】:

  • 您不能将CardView 从一个Fragment 发送到另一个。您可以做的是创建一个数据库来存储所有这些事件,然后单击plus 按钮,将事件推送到数据库并将其反映在ListFragment 中。使用 Room 设置数据库,配合 ViewModel 和 LiveData 观察数据库数据的变化。请参阅此链接:developer.android.com/training/data-storage/room

标签: java android android-fragments android-recyclerview android-cardview


【解决方案1】:

如果您想在 MyListFragment 中看到相同的 CardView,您可以让 MyListFragment 包含一个 RecyclerView,并重用相同的 EventsAdapter 和 EventsViewHolder。唯一的区别是,您不会使用数据库中“事件”的所有子项填充适配器,而是只使用您想要的单个事件填充它。

此外,由于您已使 Event 类实现可打包,因此您无需在单击加号按钮时手动创建捆绑包。

我假设您只有一个 Activity,并且您只想将 EventsFragment 替换为 MyListFragment。查看replacing one fragment with another 的文档。

第 1 步:

扩展你的onItemClickListener 看起来像:

public interface OnItemClickListener {    
    void onItemClick(int position);
    void onPlusButtonClick(int position);
}

并调整您的EventsViewHolder 构造函数中的代码,使其在单击加号按钮时如下所示:

mAddButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (listener != null) {
            // no need to manually create the bundle here
            // you already have all the information you need
            int position = getAdapterPosition();
            if (position != RecyclerView.NO_POSITION) {
                listener.onPlusButtonClick(position);
            }
        }
    }
});

第 2 步:

实现我们的新方法onPlusButtonClick。根据我们在 cmets 中的讨论,您似乎没有在任何地方实现此接口。您可以在 EventsAdapter 的构造函数中实现它:

public EventsAdapter(Context context, ArrayList<EventsItem> EventsList) {
    mEventsList = EventsList;
    mContext = context;
    mEventsListFull = new ArrayList<>(EventsList); // copy of EventsList for SearchView
    mListener = new OnItemClickListener() {
        @Override
        public void onItemClick() {
            // handle clicking the entire view holder
            // NOTE: inside your EventsViewHolder, it looks like you call this method on the entire itemView. This could 'swallow' the click on the plus button. You may need to adjust your code to handle this.
        }

        @Override
        public void onPlusButtonClick(int position) {
            MyListFragment myListFragment = new MyListFragment();
            Event event = mEventsList.get(position);
            Bundle bundle = new Bundle();
            bundle.putExtra("event", event); // this will work due to implementing parcelable
            myListFragment.setArguments(bundle);

            // use mContext since im assuming we areinside adapter
            // if in an Activity, no need to use context to get the fragment manager
            FragmentTransaction transaction = mContext.getSupportFragmentManager().beginTransaction();

            // Replace the EventsFragment with the MyListFragment
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, myListFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}

第 3 步:

在您的 MyListFragments onCreateView() 方法中:

@Override
public View onCreateView (
    LayoutInflater inflater, 
    ViewGroup container, 
    Bundle savedInstanceState
) {

    Bundle bundle = getArguments();
    Event event = bundle.getExtra("event"); // again, will work due to implementing parcelable

    // from here you should bind to a recycler view, and you can even reuse your adapter like so:
    List<EventsItem> singleEventList = new List<EventsItem>();
    singleEventList.add(event);
    EventsAdapter adapter = new EventsAdapter(getActivity(), singleEventList);

    // be sure to inflate and return your view here...
}

你应该很高兴!

为了简单起见,我在这里和那里省略了一些代码。但我希望这是可以理解的。

附带说明.. 在您的 firebase 数据库侦听器中,每次更新数据时创建一个新的 EventsAdapter 是不好的做法。相反,您应该使用新值更新适配器中的数据。为此,请在适配器内部创建一个公共方法,例如 replaceEvents(List&lt;EventsItem&gt; newEvents),并在内部将 mEventsList 替换为新事件,然后调用 notifyDataSetChanged()

【讨论】:

  • 嗨瑞恩,感谢您的评论。我对代码应该如何工作有了更好的理解,但是,它仍然不适合我。原因是因为在 onClick() 方法中,监听器始终为空。我尝试在 EventsFragment 类中调用 setOnClickListener() 来初始化监听器,但它不起作用
  • 因为监听器始终为null,所以根本不调用onPlusButtonClick方法。我的 OnItemClickListener 接口在 EventsAdapter 中,但需要在 EventsAdapter 类之外调用才能初始化监听器?
  • 如答案中所述,您实际上并没有显示您在哪里实现 OnItemClickListener.. 从您所说的看起来您似乎没有在任何地方实现它?在 EventsViewHolder.onCreateViewHolder() 方法中,将 EventsViewHolder 实例化,将 mListener 作为第三个参数传递。这是EventsAdapter 类的成员变量。最初为空,需要设置。我将修改我的答案来证明这一点。
  • 请看我对第2步的修改
  • 嗨,瑞安,再次感谢您。我想我现在大部分部分都已经启动并运行了(在休息了几天来重置我的大脑之后)。现在唯一的问题是,我不知道您所说的“R.id.fragment_container”是什么意思,因为我在系统中没有这样的id。这应该是什么?我找不到完全适合我想要的行为的布局
猜你喜欢
  • 1970-01-01
  • 2015-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
  • 2017-03-25
  • 2016-09-30
  • 2016-05-05
相关资源
最近更新 更多