Android:重启App后保留含Button与TextBox的ListView Item状态
我在主Activity里有个ListView,每个Item默认显示Button、隐藏TextBox。点击Button后,Button消失,TextBox显示"Activated by currentUser",同时这条消息会存入数据库,而且这个逻辑和ListItem的点击事件是分开的。
现在遇到的问题是:重启App后,ListView Item的状态没法保留。比如有4个Item,其中2个已经点击Button切换了状态,但重启后Button又重新显示,TextBox不见了。另外,这个App是连接MEAN Stack Web应用的,更新/删除操作会同步到MEAN端。
附上我的自定义Adapter代码:
import com.elegantcab.getcabgo.utils.QueryUtils; public class CabDriverNotificationAdapter extends ArrayAdapter<CabDriverNotification> { private SessionManager mSession; private String mNotificationId; private String mMemo; private static final String DATE_FORMAT = "LLL dd, yyyy"; private static final String TIME_FORMAT = "h:mm a"; public CabDriverNotificationAdapter(Context context, List<CabDriverNotification> notifications) { super(context, 0, notifications); } /** The static holder class will act as a holder for Android.Widget items * such as the activated by textview and the activate button **/ public static class ViewHolder{ LinearLayout _layout; TextView _activatedBy; Button acceptButton; } @Override public View getView(int position, View convertView, ViewGroup parent) { View listItemView = convertView; final ViewHolder holder; mSession = new SessionManager(getContext()); final CabDriverNotification currentNotification = getItem(position); // get the id of current notification mNotificationId = String.valueOf(currentNotification.getObjectId()); if (listItemView == null) { listItemView = LayoutInflater.from(getContext()).inflate( R.layout.notification_list_item, parent, false); holder = new ViewHolder(); holder._layout = (LinearLayout) listItemView.findViewById(R.id.activate_layout); holder._activatedBy = (TextView) listItemView.findViewById(R.id.activated_by_message); holder.activateButton = (Button) listItemView.findViewById(R.id.activate_button); listItemView.setTag(holder); //onClickListener for activate button holder.activateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMemo = toggleActivaionStatus(holder, mNotificationId); QueryUtils.updateNotification(getContext(), new SessionManager(getContext()).getNotificationUrl(), mNotificationId, mMemo); } }); } else { holder = (ViewHolder) listItemView.getTag(); holder._activatedBy = (TextView) listItemView.findViewById(R.id.activated_by_message); holder.activateButton = (Button) listItemView.findViewById(R.id.activate_button); if(currentNotification.getMemo().contains("Activated by")){ holder._activatedBy.setText(currentNotification.getMemo()); holder._activatedBy.setVisibility(View.VISIBLE); holder.activateButton.setVisibility(View.GONE); } else{ holder._activatedBy.setVisibility(View.GONE); holder.activateButton.setVisibility(View.VISIBLE); holder.activateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mMemo = toggleActivateStatus(holder, mNotificationId); QueryUtils.updateNotification(getContext(), new SessionManager(getContext()).getNotificationUrl(), mNotificationId, mMemo); } }); } } // Create full name from first and last name String fullName = currentNotification.getFirstname() + " " + currentNotification.getLastname(); // Find the TextView with view ID name TextView nameView = (TextView) listItemView.findViewById(R.id.name); // Display the name in that TextView nameView.setText(fullName); // Create a new Date object from the time in milliseconds of the notification Date dateObject = new Date(currentNotification.getTimeInMilliseconds()); // Find the TextView with view ID date TextView dateView = (TextView) listItemView.findViewById(R.id.date); // Format the date string (i.e. "Mar 3, 1984") final String formattedDate = formatDate(dateObject); // Display the date of the current notification in that TextView dateView.setText(formattedDate); // Find the TextView with view ID time TextView timeView = (TextView) listItemView.findViewById(R.id.time); // Format the time string (i.e. "4:30PM") final String formattedTime = formatTime(dateObject); // Display the time of the current notification in that TextView timeView.setText(formattedTime); } }
嘿,这个问题核心就是状态没有和数据绑定,重启后没有从服务器拉取最新状态,我给你拆解几个关键修复点:
统一Adapter的状态渲染逻辑
你现在的代码在convertView == null和else分支里分别处理状态,很容易出现逻辑不一致。应该把状态判断的代码抽出来,不管View是新创建还是复用的,都统一执行状态设置,避免复用View时状态出错。修改后的getView方法如下:@Override public View getView(int position, View convertView, ViewGroup parent) { View listItemView = convertView; final ViewHolder holder; mSession = new SessionManager(getContext()); final CabDriverNotification currentNotification = getItem(position); mNotificationId = String.valueOf(currentNotification.getObjectId()); if (listItemView == null) { listItemView = LayoutInflater.from(getContext()).inflate( R.layout.notification_list_item, parent, false); holder = new ViewHolder(); holder._layout = (LinearLayout) listItemView.findViewById(R.id.activate_layout); holder._activatedBy = (TextView) listItemView.findViewById(R.id.activated_by_message); holder.activateButton = (Button) listItemView.findViewById(R.id.activate_button); listItemView.setTag(holder); } else { holder = (ViewHolder) listItemView.getTag(); } // 核心:统一处理每个Item的显示状态 if(currentNotification.getMemo() != null && currentNotification.getMemo().contains("Activated by")){ holder._activatedBy.setText(currentNotification.getMemo()); holder._activatedBy.setVisibility(View.VISIBLE); holder.activateButton.setVisibility(View.GONE); } else { holder._activatedBy.setVisibility(View.GONE); holder.activateButton.setVisibility(View.VISIBLE); // 只在Button显示时设置点击监听,避免重复绑定 holder.activateButton.setOnClickListener(v -> { mMemo = toggleActivateStatus(holder, mNotificationId); // 关键:更新本地数据对象的状态 currentNotification.setMemo(mMemo); // 通知Adapter刷新界面 notifyDataSetChanged(); // 同步到MEAN服务器 QueryUtils.updateNotification(getContext(), mSession.getNotificationUrl(), mNotificationId, mMemo); }); } // 下面的姓名、日期、时间渲染代码保持不变 String fullName = currentNotification.getFirstname() + " " + currentNotification.getLastname(); TextView nameView = listItemView.findViewById(R.id.name); nameView.setText(fullName); Date dateObject = new Date(currentNotification.getTimeInMilliseconds()); TextView dateView = listItemView.findViewById(R.id.date); final String formattedDate = formatDate(dateObject); dateView.setText(formattedDate); TextView timeView = listItemView.findViewById(R.id.time); final String formattedTime = formatTime(dateObject); timeView.setText(formattedTime); return listItemView; }本地数据同步要及时
点击Button调用接口更新服务器后,一定要立刻更新本地currentNotification的memo字段,然后调用notifyDataSetChanged()让Adapter刷新界面。这样不仅当前界面能马上显示正确状态,内存中的数据也会同步更新,避免后续复用View时出错。启动时拉取最新数据
在主Activity的onCreate方法里,一定要确保从MEAN端拉取最新的通知列表,而不是使用旧的缓存数据。重启App后,Adapter拿到的是服务器返回的最新状态数据,自然就能正确显示每个Item的Button或TextBox了。注意空值判断
加上currentNotification.getMemo() != null的判断,避免空指针异常,毕竟服务器返回的字段可能为空。
内容的提问来源于stack exchange,提问作者Choudhury Saadmaan Mahmid

