Android中setVisibility函数失效问题求助
问题:setVisibility() 无法切换控件可见性,点击事件触发但UI无变化
我已经折腾setVisibility()好几个小时了,死活不见效。点击事件明明能正常触发,Toast提示也能正确切换(第一次点显示“Now visible”,之后交替显示提示),但那些TextView的可见性就是纹丝不动。试过加invalidate()、去掉invalidate()、换View.INVISIBLE替代View.GONE,都没用,求大佬帮忙看看!
适配器类代码
public class ExamScheduleAdapter extends ArrayAdapter<String>{ private ArrayList<String> id; private ArrayList<String> name; private ArrayList<String> date, subject, total, passing; private Activity context; Button idview; TextView nameview, dateview, subjectview, totalview, passview; public ExamScheduleAdapter(Activity context, ArrayList<String>id, ArrayList<String>name, ArrayList<String>date) { super(context, R.layout.fragment_exam_shedule, id); this.id = id; this.name = name; this.date = date; this.context = context; } public ExamScheduleAdapter(Activity context, ArrayList<String>id, ArrayList<String>name, ArrayList<String>date, ArrayList<String>subject, ArrayList<String>total, ArrayList<String>passing) { super(context, R.layout.fragment_exam_shedule, id); this.id = id; this.name = name; this.date = date; this.subject = subject; this.total = total; this.passing = passing; this.context = context; } @Override public View getView(int position, View convertView, ViewGroup parent){ LayoutInflater inflater = context.getLayoutInflater(); View listviewItem = inflater.inflate(R.layout.list_es,null,true); idview=(Button) listviewItem.findViewById(R.id.es_list_id); nameview=(TextView)listviewItem.findViewById(R.id.es_list_exam_name); dateview=(TextView)listviewItem.findViewById(R.id.es_list_date); subjectview=listviewItem.findViewById(R.id.es_list_exam_subject); totalview=listviewItem.findViewById(R.id.es_list_exam_t_marks); passview=listviewItem.findViewById(R.id.es_list_exam_p_marks); idview.setText(id.get(position)); nameview.setText(name.get(position)); dateview.setText(date.get(position)); idview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(subjectview.getVisibility()==View.GONE){ subjectview.setVisibility(View.VISIBLE); totalview.setVisibility(View.VISIBLE); passview.setVisibility(View.VISIBLE); subjectview.invalidate(); totalview.invalidate(); passview.invalidate(); Toast.makeText(getContext(), "Now visible", Toast.LENGTH_LONG).show(); } else{ subjectview.setVisibility(View.GONE); totalview.setVisibility(View.GONE); passview.setVisibility(View.GONE); subjectview.invalidate(); totalview.invalidate(); passview.invalidate(); Toast.makeText(getContext(), "Now invisible", Toast.LENGTH_LONG).show(); } } }); return listviewItem; } }
自定义列表布局代码
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:layout_marginRight="10dp"> <Button android:id="@+id/es_list_id" android:layout_height="wrap_content" android:text="Exam1" android:layout_row="0" android:layout_column="0" android:layout_columnSpan="2"/> <TextView android:layout_height="wrap_content" android:id="@+id/es_list_exam_name" android:hint="name" android:layout_row="0" android:layout_column="2" android:layout_columnSpan="5" /> <TextView android:id="@+id/es_list_date" android:layout_height="wrap_content" android:hint="2018-07-06" android:layout_row="0" android:layout_column="8" android:layout_columnSpan="3"/> <TextView android:layout_height="wrap_content" android:id="@+id/es_list_exam_subject" android:hint="Hindi" android:layout_row="1" android:layout_column="0" android:visibility="gone"/> <TextView android:layout_height="wrap_content" android:id="@+id/es_list_exam_t_marks" android:hint="100" android:layout_row="1" android:layout_column="4" android:visibility="gone"/> <TextView android:layout_height="wrap_content" android:id="@+id/es_list_exam_p_marks" android:hint="35" android:layout_row="1" android:layout_column="8" android:visibility="gone"/> </GridLayout>
解决方案:问题出在ListView视图复用+全局控件引用混乱
嘿,兄弟,我一眼就瞅到问题所在了——你把控件引用存在了适配器的全局成员变量里,这在ListView复用视图的时候直接乱套!
核心问题分析
ListView的convertView是用来复用视图的,你每次调用getView都把当前Item的控件赋值给适配器的全局变量subjectview、totalview这些,当滚动列表的时候,这些变量会被后面的Item覆盖。等你点击某个按钮的时候,你操作的根本不是当前点击的那个Item里的控件,而是最后一次getView创建的那个Item的控件,所以你看不到任何变化!
修复步骤
- 把控件变量移到
getView方法内部,不要用适配器的全局变量存这些控件引用,每个Item的控件都应该是当前listviewItem里的局部引用。 - 在点击事件里,通过
v.getParent()获取当前点击的Item视图,然后重新findViewById拿到当前Item的控件,这样操作的才是正确的控件。
修改后的getView方法代码如下:
@Override public View getView(int position, View convertView, ViewGroup parent){ LayoutInflater inflater = context.getLayoutInflater(); View listviewItem = inflater.inflate(R.layout.list_es,null,true); // 把控件改成局部变量,避免全局引用混乱 Button idview=(Button) listviewItem.findViewById(R.id.es_list_id); TextView nameview=(TextView)listviewItem.findViewById(R.id.es_list_exam_name); TextView dateview=(TextView)listviewItem.findViewById(R.id.es_list_date); idview.setText(id.get(position)); nameview.setText(name.get(position)); dateview.setText(date.get(position)); idview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 从当前点击按钮的父视图中获取对应Item的控件 View parentItem = (View) v.getParent(); TextView currentSubject = parentItem.findViewById(R.id.es_list_exam_subject); TextView currentTotal = parentItem.findViewById(R.id.es_list_exam_t_marks); TextView currentPass = parentItem.findViewById(R.id.es_list_exam_p_marks); if(currentSubject.getVisibility()==View.GONE){ currentSubject.setVisibility(View.VISIBLE); currentTotal.setVisibility(View.VISIBLE); currentPass.setVisibility(View.VISIBLE); Toast.makeText(getContext(), "Now visible", Toast.LENGTH_LONG).show(); } else{ currentSubject.setVisibility(View.GONE); currentTotal.setVisibility(View.GONE); currentPass.setVisibility(View.GONE); Toast.makeText(getContext(), "Now invisible", Toast.LENGTH_LONG).show(); } // 这里完全不需要invalidate(),setVisibility已经会触发UI刷新 } }); return listviewItem; }
额外优化建议
建议使用ViewHolder模式来优化ListView性能,避免每次getView都调用findViewById,同时彻底杜绝控件引用混乱的问题:
static class ViewHolder { Button idBtn; TextView nameTv, dateTv, subjectTv, totalTv, passTv; } @Override public View getView(int position, View convertView, ViewGroup parent){ ViewHolder holder; if(convertView == null){ LayoutInflater inflater = context.getLayoutInflater(); convertView = inflater.inflate(R.layout.list_es,null,true); holder = new ViewHolder(); holder.idBtn = convertView.findViewById(R.id.es_list_id); holder.nameTv = convertView.findViewById(R.id.es_list_exam_name); holder.dateTv = convertView.findViewById(R.id.es_list_date); holder.subjectTv = convertView.findViewById(R.id.es_list_exam_subject); holder.totalTv = convertView.findViewById(R.id.es_list_exam_t_marks); holder.passTv = convertView.findViewById(R.id.es_list_exam_p_marks); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.idBtn.setText(id.get(position)); holder.nameTv.setText(name.get(position)); holder.dateTv.setText(date.get(position)); // 操作当前ViewHolder里的控件,完全不会混乱 holder.idBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(holder.subjectTv.getVisibility()==View.GONE){ holder.subjectTv.setVisibility(View.VISIBLE); holder.totalTv.setVisibility(View.VISIBLE); holder.passTv.setVisibility(View.VISIBLE); Toast.makeText(getContext(), "Now visible", Toast.LENGTH_LONG).show(); } else{ holder.subjectTv.setVisibility(View.GONE); holder.totalTv.setVisibility(View.GONE); holder.passTv.setVisibility(View.GONE); Toast.makeText(getContext(), "Now invisible", Toast.LENGTH_LONG).show(); } } }); return convertView; }
这样不仅解决了你的可见性问题,还提升了列表的滚动性能,一举两得!
内容的提问来源于stack exchange,提问作者Mihir Tanksale
相关产品推荐
相关产品推荐

