探究Android中ListView复用导致布局错乱的解决方案
首先来说一下具体的需求是什么样的:
需求如图所示,这里面有abcd四个选项的题目,当点击a选项,如果a是正确的答案,则变成对勾的图案,如果是错误答案,则变成错误的图案,这里当时在写的时候觉得很简单,只要是在点击的时候判断我点击的选项与正确答案是否一样,是一样就将图片换成正确的样式,如果不一样就换成错误的样式,于是我便写了下面的代码(只贴出了核心adapter中的代码)
package com.fizzer.anbangproject_dahuo_test.adapter; import android.annotation.targetapi; import android.content.context; import android.graphics.drawable.drawable; import android.os.build; import android.text.textutils; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.textview; import com.fizzer.anbangproject_dahuo_test.model.convertmodel; import com.fizzer.anbangproject_dahuo_test.r; import java.util.list; /** * created by fizzer on 2016/10/8. * email: doraemonmqq@sina.com */ public class convertviewadapter extends baseadapter { private list<convertmodel> list; private context mcontext; public convertviewadapter(context context, list<convertmodel> list) { mcontext = context; this.list = list; } @override public int getcount() { if (list == null) { return 0; } else { return list.size(); } } @override public view getview(int position, view convertview, viewgroup parent) { viewholder mviewholder; if (convertview == null) { convertview = view.inflate(mcontext, r.layout.view_upgradepartnet_topic_layout, null); mviewholder = new viewholder(); mviewholder.tvtitle = (textview) convertview.findviewbyid(r.id.tvtitle); mviewholder.tvselecta = (textview) convertview.findviewbyid(r.id.tvselecta); mviewholder.tvselectb = (textview) convertview.findviewbyid(r.id.tvselectb); mviewholder.tvselectc = (textview) convertview.findviewbyid(r.id.tvselectc); mviewholder.tvselectd = (textview) convertview.findviewbyid(r.id.tvselectd); convertview.settag(mviewholder); } else { mviewholder = (viewholder) convertview.gettag(); } convertmodel module = list.get(position); mviewholder.tvtitle.settext("q" + (position + 1) + ":" + module.title); mviewholder.tvselecta.settext(module.optiona); mviewholder.tvselectb.settext(module.optionb); mviewholder.tvselectc.settext(module.optionc); mviewholder.tvselectd.settext(module.optiond); initlistener(mviewholder, module.rightoption, position, module); return convertview; } @override public object getitem(int position) { return null; } @override public long getitemid(int position) { return 0; } private void initlistener(final viewholder mviewholder, final string select, final int position, final convertmodel module) { mviewholder.tvselecta.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { judgeselect(mviewholder, mviewholder.tvselecta, "a", select, position); } }); mviewholder.tvselectb.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { judgeselect(mviewholder, mviewholder.tvselectb, "b", select, position); } }); mviewholder.tvselectc.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { judgeselect(mviewholder, mviewholder.tvselectc, "c", select, position); } }); mviewholder.tvselectd.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { judgeselect(mviewholder, mviewholder.tvselectd, "d", select, position); } }); } private void clearselectstate(viewholder mviewholder) { mviewholder.tvselecta.setcompounddrawables(getdrawableresource(r.drawable.ic_select_a), null, null, null); mviewholder.tvselectb.setcompounddrawables(getdrawableresource(r.drawable.ic_select_b), null, null, null); mviewholder.tvselectc.setcompounddrawables(getdrawableresource(r.drawable.ic_select_c), null, null, null); mviewholder.tvselectd.setcompounddrawables(getdrawableresource(r.drawable.ic_select_d), null, null, null); } private void judgeselect(viewholder viewholder, textview text, string select, string rightselect, int position) { //清楚之前的状态 clearselectstate(viewholder); if (select.equals(rightselect)) { text.setcompounddrawables(getdrawableresource(r.drawable.ic_select_right), null, null, null); } else { text.setcompounddrawables(getdrawableresource(r.drawable.ic_select_error), null, null, null); } } @targetapi(build.version_codes.lollipop) private drawable getdrawableresource(int res) { drawable drawable = mcontext.getdrawable(res); drawable.setbounds(0, 0, drawable.getminimumwidth(), drawable.getminimumheight()); return drawable; } class viewholder { textview tvtitle; textview tvselecta; textview tvselectb; textview tvselectc; textview tvselectd; } }
写完这段代码信心满满,觉得没问题了,但是在手机上一运行,发现出问题了,效果如下:
是的,由于listview的布局复用机制,导致下面没有选择的条目也因为复用而选择了选项
其实解决的方法很简单,就是将这个选中的条目与该条目对应的model相关联起来,具体怎么做呢,下面来仔细的分析分析,
首先在创建model的时候添加一个默认的字段,这个字段就是你选择的选项,当然初始值是没有的,在getview中对布局进行初始化的时候,就去判断这个字段是否有值,并且值为多少,如果有值,就去判断值为正确还是为错误,为正确则替换成正确的图片,如果为错误,则替换成错误的图片,如果没有值,则显示原始的abcd四种初始化图片,这样,问题就迎刃而解了
下面贴出完整的代码,其实就跟上面的代码是差不多的,只不过在对model中添加的那个字段进行了一些复制与判断
package com.fizzer.anbangproject_dahuo_test.adapter; import android.annotation.targetapi; import android.content.context; import android.graphics.drawable.drawable; import android.os.build; import android.text.textutils; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.textview; import com.fizzer.anbangproject_dahuo_test.model.convertmodel; import com.fizzer.anbangproject_dahuo_test.r; import java.util.list; /** * created by fizzer on 2016/10/8. * email: doraemonmqq@sina.com */ public class convertviewadapter extends baseadapter { private list<convertmodel> list; private context mcontext; public convertviewadapter(context context, list<convertmodel> list) { mcontext = context; this.list = list; } @override public int getcount() { if (list == null) { return 0; } else { return list.size(); } } @override public view getview(int position, view convertview, viewgroup parent) { viewholder mviewholder; if (convertview == null) { convertview = view.inflate(mcontext, r.layout.view_upgradepartnet_topic_layout, null); mviewholder = new viewholder(); mviewholder.tvtitle = (textview) convertview.findviewbyid(r.id.tvtitle); mviewholder.tvselecta = (textview) convertview.findviewbyid(r.id.tvselecta); mviewholder.tvselectb = (textview) convertview.findviewbyid(r.id.tvselectb); mviewholder.tvselectc = (textview) convertview.findviewbyid(r.id.tvselectc); mviewholder.tvselectd = (textview) convertview.findviewbyid(r.id.tvselectd); convertview.settag(mviewholder); } else { mviewholder = (viewholder) convertview.gettag(); } convertmodel module = list.get(position); mviewholder.tvtitle.settext("q" + (position + 1) + ":" + module.title); mviewholder.tvselecta.settext(module.optiona); mviewholder.tvselectb.settext(module.optionb); mviewholder.tvselectc.settext(module.optionc); mviewholder.tvselectd.settext(module.optiond); initlistener(mviewholder, module.rightoption, position, module); <span style="color:#cc0000;">if (textutils.isempty(module.check)) { clearselectstate(mviewholder); } else { judgeselect(mviewholder, getchecktextview(mviewholder, module.check), module.check, module.rightoption, position); }</span> return convertview; } @override public object getitem(int position) { return null; } @override public long getitemid(int position) { return 0; } private void initlistener(final viewholder mviewholder, final string select, final int position, final convertmodel module) { mviewholder.tvselecta.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { <span style="color:#cc0000;">module.check = "a";</span> judgeselect(mviewholder, mviewholder.tvselecta, "a", select, position); } }); mviewholder.tvselectb.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { <span style="color:#cc0000;">module.check = "b";</span> judgeselect(mviewholder, mviewholder.tvselectb, "b", select, position); } }); mviewholder.tvselectc.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { <span style="color:#cc0000;">module.check = "c"; </span> judgeselect(mviewholder, mviewholder.tvselectc, "c", select, position); } }); mviewholder.tvselectd.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { <span style="color:#cc0000;">module.check = "d";</span> judgeselect(mviewholder, mviewholder.tvselectd, "d", select, position); } }); } private void clearselectstate(viewholder mviewholder) { mviewholder.tvselecta.setcompounddrawables(getdrawableresource(r.drawable.ic_select_a), null, null, null); mviewholder.tvselectb.setcompounddrawables(getdrawableresource(r.drawable.ic_select_b), null, null, null); mviewholder.tvselectc.setcompounddrawables(getdrawableresource(r.drawable.ic_select_c), null, null, null); mviewholder.tvselectd.setcompounddrawables(getdrawableresource(r.drawable.ic_select_d), null, null, null); } private void judgeselect(viewholder viewholder, textview text, string select, string rightselect, int position) { //清楚之前的状态 clearselectstate(viewholder); if (select.equals(rightselect)) { text.setcompounddrawables(getdrawableresource(r.drawable.ic_select_right), null, null, null); } else { text.setcompounddrawables(getdrawableresource(r.drawable.ic_select_error), null, null, null); } } @targetapi(build.version_codes.lollipop) private drawable getdrawableresource(int res) { drawable drawable = mcontext.getdrawable(res); drawable.setbounds(0, 0, drawable.getminimumwidth(), drawable.getminimumheight()); return drawable; } <span style="color:#cc0000;">private textview getchecktextview(viewholder mviewholder, string rightselect) { if ("a".equals(rightselect)) { return mviewholder.tvselecta; } else if ("b".equals(rightselect)) { return mviewholder.tvselectb; } else if ("c".equals(rightselect)) { return mviewholder.tvselectc; } else if ("d".equals(rightselect)) { return mviewholder.tvselectd; } return null; }</span> class viewholder { textview tvtitle; textview tvselecta; textview tvselectb; textview tvselectc; textview tvselectd; } }
其中标红的就是新添的代码,加上这些后,问题就解决了,来看一下解决后的代码运行情况:
总结:
最后来总结一下这个问题的解决思路吧:
首先就是需要在该填充器对应的实体类中添加一个选中的(check)字段,在进行getview操作中,去根据这个check字段来进行相应的操作,如过有值,则设置成对应的样式,如果没有值,则设置成没有值得样式,当然,在用户点击的时候,要及时的对该字段进行赋值,类似的,像listview中有checkbox也可以采用同样的方法来进行解决。
以上所述是小编给大家介绍的探究android中listview复用导致布局错乱的解决方案,希望对大家有所帮助