[英]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: 我想也许这可能会有所帮助:
onBindViewHolder()
. 在onBindViewHolder()
上的按钮上设置单击侦听器。 notifyItemChanged(position)
在你的点击监听器里面调用notifyItemChanged(position)
shouldChangeState = true
保持适配器中的某些状态以处理何时更改按钮状态的逻辑,例如,在onClick中有一个var,你可以标记为shouldChangeState = true
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. 由于适配器中的mEventListResponseList
和mAllRSVPEventsList
是私有的,因此更改它们的唯一方法是调用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
方式更新适配器中的mEventListResponseList
和mAllRSVPEventsList
,并使用来自响应的数据调用它。 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.