简体   繁体   English

在活动/片段中确认警报对话框后,如何在单击时更新我的​​ RecyclerView 按钮状态?

[英]How do I update my RecyclerView button state on click after alert dialog confirmation in activity/fragment?

I have a pretty unique situation, where I have a button in a recyclerview, which upon click (initial state "register"), passes an intent to a broadcast receiver in fragment or activity where it throws an alert dialog , with 2 options, yes or no.我有一个非常独特的情况,我在回收视图中有一个按钮,点击后(初始状态“注册”),将意图传递给片段或活动中的广播接收器,在那里它抛出一个警报对话框,有 2 个选项,是的或没有。 If no is selected, nothing happens and the dialog dismisses, but if yes is clicked, it processes a function I defined in my presenter class (related to data) and is supposed to update my button ui state to "cancel".如果选择 no,则没有任何反应并且对话框关闭,但是如果单击 yes,它会处理我在演示器类中定义的函数(与数据相关),并且应该将我的按钮 ui 状态更新为“取消”。 and same goes for the other way around where upon clicking cancel it will bring back alert, and clicking on yes will switch the ui text to register.反之亦然,点击取消后会返回警报,点击是将切换 ui 文本以进行注册。

Now I have implemented the code as follows.(please note, even notifydatasetchanged doesnt work for me).现在我已经实现了如下代码。(请注意,即使 notifydatasetchanged 对我也不起作用)。 Any idea what I am doing wrong and how I can achieve this?知道我做错了什么以及如何实现这一目标吗? code in my public void onBind(int position) function in adapter:适配器中我的public void onBind(int position)函数中的代码:

if (repo.getIsRsvpAvailable().equals("true")) {
    rsvpButton.setVisibility(View.VISIBLE);
    for (int i = 0; i < mAllRSVPEventsList.size(); i++) {
        if (mAllRSVPEventsList.get(i).getEvent().getEventId().equals(repo.getEventId()) && mAllRSVPEventsList.get(i).getIsAttending()) {
            rsvpButton.setText("CANCEL");
            rsvpButton.setOnClickListener(v -> {
                Intent in = new Intent("main_rsvp_button_clicked");
                in.putExtra("main_rsvp_event_id", repo.getEventId());
                in.putExtra("main_rsvp_is_attending", "false");
                in.putExtra("main_rsvp_item_position", position);
                rsvpButton.getContext().sendBroadcast(in);
            });
            break;
        } else {
            rsvpButton.setText("RSVP");
            rsvpButton.setOnClickListener(v -> {
                Intent in = new Intent("main_rsvp_button_clicked");
                in.putExtra("main_rsvp_event_id", repo.getEventId());
                in.putExtra("main_rsvp_is_attending", "true");
                in.putExtra("main_rsvp_item_position", position);

                rsvpButton.getContext().sendBroadcast(in);
            });
        }
    }

}

Here's the corresponding code in broadcast receiver in my activity :这是我的活动中广播接收器中的相应代码:

private BroadcastReceiver mEventIdReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            String eventId = intent.getStringExtra(EVENTID_MAIN_EXTRA_TITLE);
            String isAttending = intent.getStringExtra(EVENTID_MAIN_IS_ATTENDING);
            int itemPosition = intent.getIntExtra(EVENTID_MAIN_RSVP_ITEM_POSITION, 0);


            if (isAttending.equals("true")) {
                showDialog(R.string.rsvp_title, R.string.confirm_rsvp_body, R.string.yes,
                        (dialog, which) -> {
                            mPresenter.onRSVPClick(eventId, isAttending);

                            mEventListAdapter.notifyDataSetChanged();
                            mEventRecyclerView.removeAllViews();
                            mEventRecyclerView.scrollToPosition(itemPosition);

                        }, R.string.no, null, null);
            } else {
                showDialog(R.string.rsvp_title, R.string.confirm_cancel_body, R.string.yes,
                        (dialog, which) -> {
                            mPresenter.onRSVPClick(eventId, isAttending);

                            mEventListAdapter.notifyDataSetChanged();
                            mEventRecyclerView.removeAllViews();
                            mEventRecyclerView.scrollToPosition(itemPosition);

                        }, R.string.no, null, null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};

Please note: mEventListAdapter is my adapter i am using the button UI code in and mEventRecyclerView is the recycler view i am using in the fragment.请注意:mEventListAdapter 是我的适配器,我在其中使用了按钮 UI 代码,而 mEventRecyclerView 是我在片段中使用的回收器视图。

Any idea what I am missing or doing wrong?知道我错过了什么或做错了什么吗? Thanks!谢谢!

RSVPClick method : RSVPClick 方法:

@Override
public void onRSVPClick(String eventId, String isAttending) {
    getMvpView().showLoading();
    getCompositeDisposable().add(getDataManager()
            .doRSVPEventApiCall(
                    eventId,
                    getDataManager().getFirstName(),
                    getDataManager().getLastName(),
                    getDataManager().getCurrentUserEmail(),
                    isAttending
            )
            .subscribeOn(getSchedulerProvider().io())
            .observeOn(getSchedulerProvider().ui())
            .subscribe(response -> {
                if (!isViewAttached()) {
                    return;
                }
                getMvpView().hideLoading();
            }, throwable -> {
                if (!isViewAttached()) {
                    return;
                }
                getMvpView().hideLoading();
                if (throwable instanceof ANError) {
                    ANError anError = (ANError) throwable;
                    handleApiError(anError);
                }
            }));
}

You can easily maintain an array to indicate the state of the buttons of each of your items in the RecyclerView . 您可以轻松维护一个数组,以指示RecyclerView中每个项目的按钮状态。 For example, let us assume there are two states of each button (ie RSVP = 0, Cancel = 1). 例如,让我们假设每个按钮有两种状态(即RSVP = 0,取消= 1)。 Now declare an array in your RecyclerView adapter like the following. 现在在RecyclerView适配器中声明一个数组,如下所示。

private int[] stateArray; 

In the constructor of your adapter, initialize the array like the following. 在适配器的构造函数中,初始化数组,如下所示。 Let us assume your constructor looks like the following. 让我们假设您的构造函数如下所示。

public EventListAdapter(Context context, ArrayList<Event> eventList) {
    this.eventList = eventList; 
    stateArray = new int[eventList.size];
    initializeTheStateArray(eventList);
}

private void initializeTheStateArray(ArrayList<Event> eventList) {

    // Initialize the array so that we can keep track of the items which are being attended or not. 
    for (int i = 0; i < eventList.size(); i++) {
        if(event.isAttending()) stateArray[i] = 1;
        else stateArray[i] = 0;
    }
}

Now in the onBindViewHolder function, set the text of the button based on the entry of the stateArray . 现在在onBindViewHolder函数中,根据stateArray的条目设置按钮的文本。 This should look somewhat like the following. 这看起来应该如下所示。

if(stateArray[position] == 1) button.setText("Cancel"); // Because this item is already registerd 
else button.setText("RSVP");

You need some additional function in your adapter so that you can update the stateArray on your button click or from an update from the API. 您需要在适配器中使用一些附加功能,以便可以在按钮单击或API更新中更新stateArray

public void updateButtonState(int position, boolean isAttendening) {
    if(isAttending) stateArray[position] = 1;
    else stateArray[position] = 0;
    notifyDataSetChanged(); // Call the notifyDataSetChanged here to see the affect
}

And when you update the whole list of your adapter after an API call, do not forget the update the stateArray as well. 当您在API调用后更新适配器的整个列表时,不要忘记更新stateArray I hope you have a function to update the event list in your adapter already. 我希望你有一个功能来更新你的适配器中的事件列表。 Modify the function like the following. 修改如下所示的功能。

public void updateEventList(ArrayList<Event> eventList) {
    this.eventList = eventList;
    stateArray = new int[eventList.size()];
    initializeTheStateArray(eventList);
}

Now you can call updateButtonState function of your adapter when the button is clicked. 现在,您可以在单击按钮时调用适配器的updateButtonState函数。 Modify the button click action in your rsvpButton.setOnClickListener . 修改rsvpButton.setOnClickListener的按钮单击操作。

You can also modify the onReceive function if necessary to get the expected output in your RecyclerView . 如有必要,您还可以修改onReceive函数以获取RecyclerView的预期输出。

Hope this setup will help you to achieve the expected behavior that you want. 希望此设置可以帮助您实现所需的预期行为。

Last, but not least, in your onReceive function, you are immediately updating the RecyclerView when it will have no effect on the items of the RecyclerView because the network call is asynchronous and will take some time to fetch the data from the API. 最后,但并非最不重要的是,在onReceive函数中,当它对RecyclerView的项目没有影响时,您立即更新RecyclerView因为网络调用是异步的,并且需要一些时间从API获取数据。 You might consider calling the updateEventList method from the onRSVPClick method when the data is available from the API call. 当数据可从API调用获得时,您可以考虑从onRSVPClick方法调用updateEventList方法。

Hope that helps! 希望有所帮助!

I think that perhaps this might help: 我想也许这可能会有所帮助:

  1. Set click listener on the button in onBindViewHolder() . onBindViewHolder()上的按钮上设置单击侦听器。
  2. Inside your click listener call notifyItemChanged(position) 在你的点击监听器里面调用notifyItemChanged(position)
  3. Maintain some state in your adapter to handle the logic for when to change the buttons state, for instance, have a var in the onClick you can flag like shouldChangeState = true 保持适配器中的某些状态以处理何时更改按钮状态的逻辑,例如,在onClick中有一个var,你可以标记为shouldChangeState = true
  4. When on onBindViewHolder() is called again, check this state and bind as you would usually only handle this scenario and change TextView accordingly. 当再次调用onBindViewHolder()时,检查此状态并绑定,因为您通常只处理此场景并相应地更改TextView。 button.setVisibility(..) or button.text = "My New Text" button.setVisibility(..)button.text = "My New Text"

Inside onBindViewholder : 在onBindViewholder里面:

 holder.myRow.setOnClickListener(v -> {
          notifyItemChanged(position) 
 }

Ok, so to handle your holder inside your onBindViewHolder (In Kotlin Sorry): 好的,所以要在你的onBindViewHolder中处理你的持有人(在Kotlin抱歉):

Create your view holder. 创建视图持有者。

sealed class MyListViewHolder(view: View) : RecyclerView.ViewHolder(view)

class MyListItemViewHolder(view: View) : MyListViewHolder(view) {
    val name: TextView = view.my_name
    val myDetail: TextView = view.my_detail
    val myOtherDetail: TextView = view.my_other_detail
}

In on create: 在创建:

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyListViewHolder {
        val inflater: LayoutInflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(R.layout.row_item, parent, false)
        val viewHolder = MyListItemViewHolder(view)

        with(view) {
            tag = viewHolder
        }
        return viewHolder
    }

Then in onBindViewHolder: 然后在onBindViewHolder上:

  override fun onBindViewHolder(itemHolder: MyListViewHolder, position: Int) {
        val holder = itemHolder as MyListItemViewHolder
        val currentInfo = myList[position]

        // Get your data from list to bind here.

        holder.name.text = // Text
        holder.myDetail.text = // Text
        holder.myOtherDetail.text = Text

        holder.setOnClickListener {
             // Other stuff, state, actions etc.
             notifyItemChanged(position)
        }
    }

notifyDatasetChanged() should be called after the data set is actually changed. 实际更改数据集后应调用notifyDatasetChanged() As Sudhanshu Gupta pointed out notifyDatasetChanged() is called at the wrong time (when the dialog button is clicked). 正如Sudhanshu Gupta所指出的,在错误的时间调用notifyDatasetChanged() (单击对话框按钮时)。 Since your mEventListResponseList and mAllRSVPEventsList in the adapter are private the only way to change them is to call addItems() and addRSVPItems() methods. 由于适配器中的mEventListResponseListmAllRSVPEventsList是私有的,因此更改它们的唯一方法是调用addItems()addRSVPItems()方法。 Clearly at the time of dialog button click, none of these methods are called indicating data set is not changed. 显然,在单击对话框按钮时,不会调用这些方法,表示数据集未更改。 So notifyDatasetChanged() has no effect. 因此notifyDatasetChanged()无效。

it might take a few seconds to update. 更新可能需要几秒钟。 but regardless of whether it updates or not, is it possible to change the ui of the button in adapter to a different state (say cancel from rsvp) once alert appears and yes is clicked 但无论是否更新,都可以在出现警报并单击是时将适配器中的按钮的ui更改为其他状态(例如从rsvp取消)

No, because of the above reason. 不,因为上述原因。

And, when the response arrives it is discarded except for logging. 并且,当响应到达时,除了记录之外它将被丢弃。 Snippet from onRSVPClick() in the Presenter class. 来自Presenter类中的onRSVPClick()片段。

.subscribe(response -> {
    if (!isViewAttached()) {
        return;
    }
    Log.d("rsvptest", "basic event id:" + response);

    getMvpView().hideLoading();

}

To see the change, you should somehow update mEventListResponseList and mAllRSVPEventsList in the adapter by exposing a suitable method and call it with the data from the arrived response. 要查看更改,您应该以mEventListResponseList方式更新适配器中的mEventListResponseListmAllRSVPEventsList ,并使用来自响应的数据调用它。 Also don't forget to call notifyDatasetChanged() in that method too. 另外,不要忘记在该方法中调用notifyDatasetChanged()

So, I was able to come up with an answer and was pretty straightforward. 所以,我能够得出一个答案而且很简单。

Here's the answer: 这是答案:

if (repo.getIsRsvpAvailable().equals("true")){
    rsvpButton.setVisibility(View.VISIBLE);
    if(mAllRSVPEventsList.size() != 0) {

        for (int i = 0; i < mAllRSVPEventsList.size(); i++) {
            if (mAllRSVPEventsList.get(i).getEvent().getEventId().equals(repo.getEventId()) && mAllRSVPEventsList.get(i).getIsAttending()) {
                rsvpButton.setText("CANCEL");
                rsvpButton.setTextColor(Color.parseColor("#647ed6"));
                mYesText.setVisibility(View.VISIBLE);
                rsvpButton.setBackgroundColor(Color.WHITE);
                GradientDrawable gd = new GradientDrawable();
                gd.setCornerRadius(2);
                gd.setGradientRadius(2);
                gd.setStroke(2, 0xFF647ed6);
                rsvpButton.setBackground(gd);
                rsvpButton.setOnClickListener(v -> {
                    Log.d("clickedbutton", "it is in" + repo.getTitle());
                    mCallback.onRSVPButtonClick(repo.getEventId(), position, "false", repo.getTitle(), rsvpButton, mAllRSVPEventsList);

                });
                break;
            }

            if (mAllRSVPEventsList.get(i).getEvent().getEventId().equals(repo.getEventId()) && !mAllRSVPEventsList.get(i).getIsAttending()) {

                rsvpButton.setText("RSVP");
                rsvpButton.setBackgroundColor(Color.parseColor("#647ed6"));
                rsvpButton.setTextColor(Color.WHITE);
                mYesText.setVisibility(View.GONE);

                rsvpButton.setOnClickListener(v -> {

                    mCallback.onRSVPButtonClick(repo.getEventId(), position, "true", repo.getTitle(), rsvpButton, mAllRSVPEventsList);

                });
                break;
            } else {
                rsvpButton.setText("RSVP");
                rsvpButton.setBackgroundColor(Color.parseColor("#647ed6"));
                rsvpButton.setTextColor(Color.WHITE);
                mYesText.setVisibility(View.GONE);
                rsvpButton.setOnClickListener(v -> {
                    mCallback.onRSVPButtonClick(repo.getEventId(), position, "true", repo.getTitle(), rsvpButton, mAllRSVPEventsList);

                });
            }


        }
    } else {
        for (int i = 0; i < mEventListResponseList.size(); i++) {
            rsvpButton.setOnClickListener(v -> {
                mCallback.onRSVPButtonClick(repo.getEventId(), position, "true", repo.getTitle(), rsvpButton, mAllRSVPEventsList);

            });
        }
    }
}

So, basically I had to separate out the if loop so that the ui updates based on conditions after refresh. 所以,基本上我必须将if循环分开,以便ui在刷新后根据条件进行更新。 Works every time without any issue and does check if the size of the rsvp list if 0, if it is(set to rsvp by default, clicking on which will switch it to cancel button in UI), it adds the event to the rsvp list so next time I iterate, it has the event to cross check against. 每次都没有任何问题,并检查rsvp列表的大小是否为0,如果是(默认设置为rsvp,单击将其切换为UI中的取消按钮),它会将事件添加到rsvp列表所以下次我迭代时,它有交叉检查的事件。 Thanks to all for trying to help with this, +10 for all those who answered! 感谢大家帮忙解决这个问题,所有回答的人都是+10! I appreciate your help. 我感谢您的帮助。

Are you updating the repo object based on user selected action on the dialog before calling notifyDatasetChanged() ? 在调用notifyDatasetChanged()之前,您是否根据用户在对话框上选择的操作更新了repo对象? Looks like the doRSVPEventApiCall() method does not update the list of items available to the adapter thus notifyDatasetChanged() has no effect. 看起来doRSVPEventApiCall()方法不会更新适配器可用的项目列表,因此notifyDatasetChanged()无效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在警报对话框和片段之间进行通信? - How do I communicate between alert dialog back to my fragment? 在我的活动(SQLite)中修改数据后如何更新我的片段? - How do I update my Fragment after data modification in my activity (SQLite)? 如何更新片段中的 recyclerView - How do I update a recyclerView in a fragment 如何在对话框单击时打开片段? - How do I open a Fragment on dialog click? 单击通知中的按钮时如何更新活动 UI? - How to update activity UI when i click a button in my notification? 单击按钮获取api后如何在片段中显示recyclerview - how to display recyclerview in a fragment after fetching an api on the click of a button 在Android中关闭提醒对话框后,如何在活动调用中刷新TextView? - How do I refresh TextView on activity calling after closing alert dialog in Android? 如何更新片段中的 recyclerview 列表? (notifyDataSetChanged 不起作用) - How do I update the recyclerview list in the fragment? (notifyDataSetChanged not working) 如何在选项卡式活动的片段中获取“计算按钮” - How do I get my “calculate button” in a Fragment of a Tabbed Activity working 关闭活动后如何更新片段 UI - How can I update the fragment UI, after closing the activity
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM