原始错误代码:
holder.mEdtProductQuantity.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().length() > 0) {
try {
holder.mEdtProductQuantity.setTextColor(getResources().getColor(R.color.greyBlack));
mProducts.get(position).quantity = Integer.parseInt(
s.toString().replace(StringUtils.Comma, ""));
} catch (Exception e) {
holder.mEdtProductQuantity.setTextColor(Color.RED);
}
} else {
mProducts.get(position).quantity = -1;
}
}
@Override
public void afterTextChanged(Editable s) {
if (!StringUtils.touzi_ed_values22.equals(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""))) {
holder.mEdtProductQuantity.setText(
StringUtils.addComma(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""),
holder.mEdtProductQuantity));
holder.mEdtProductQuantity.setSelection(
StringUtils.addComma(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""),
holder.mEdtProductQuantity).length());
}
}
});
holder.mEdtProductQuantity.setText(product.quantity < 0 ? "" : String.valueOf(product.quantity));
修改之后:
// 如果已经绑定文本变化监听器不再次绑定 if (holder.mEdtProductQuantity.getTag() instanceof TextWatcher){ holder.mEdtProductQuantity.removeTextChangedListener((TextWatcher)holder.mEdtProductQuantity.getTag()); } holder.mEdtProductQuantity.setText(product.quantity < 0 ? "" : String.valueOf(product.quantity)); TextWatcher watcher=new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.toString().length() > 0) { try { holder.mEdtProductQuantity.setTextColor(getResources().getColor(R.color.greyBlack)); mProducts.get(position).quantity = Integer.parseInt( s.toString().replace(StringUtils.Comma, "")); } catch (Exception e) { holder.mEdtProductQuantity.setTextColor(Color.RED); } } else { mProducts.get(position).quantity = -1; } } @Override public void afterTextChanged(Editable s) { // 如果填入数据与缓存数据相同返回 if(!StringUtils.touzi_ed_values22.equals(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""))) { holder.mEdtProductQuantity.setText( StringUtils.addComma(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""), holder.mEdtProductQuantity)); holder.mEdtProductQuantity.setSelection( StringUtils.addComma(holder.mEdtProductQuantity.getText().toString().trim().replaceAll(StringUtils.Comma, ""), holder.mEdtProductQuantity).length()); } } }; holder.mEdtProductQuantity.addTextChangedListener(watcher); holder.mEdtProductQuantity.setTag(watcher);
StringUtils的工具类,此处也贴上,供大家参考使用。
public class StringUtils { private StringUtils() { throw new AssertionError(); } public static String touzi_ed_values22 = ""; public static final String Comma = ","; /** * 在数字型字符串千分位加逗号 * * @param str * @param edtext * @return sb.toString() */ public static String addComma(String str, EditText edtext) { touzi_ed_values22 = edtext.getText().toString().trim().replaceAll(Comma, ""); boolean neg = false; if (str.startsWith("-")) { //处理负数 str = str.substring(1); neg = true; } String tail = null; if (str.indexOf('.') != -1) { //处理小数点 tail = str.substring(str.indexOf('.')); str = str.substring(0, str.indexOf('.')); } StringBuilder sb = new StringBuilder(str); sb.reverse(); for (int i = 3; i < sb.length(); i += 4) { sb.insert(i, Comma); } sb.reverse(); if (neg) { sb.insert(0, '-'); } if (tail != null) { sb.append(tail); } return sb.toString(); } /** * is null or its length is 0 or it is made by space * <p> * <pre> * isBlank(null) = true; * isBlank("") = true; * isBlank(" ") = true; * isBlank("a") = false; * isBlank("a ") = false; * isBlank(" a") = false; * isBlank("a characters") = false; * </pre> * * @param str * @return if string is null or its size is 0 or it is made by space, return true, else return false. */ public static boolean isBlank(String str) { return (str == null || str.trim().length() == 0); } /** * is null or its length is 0 * <p> * <pre> * isEmpty(null) = true; * isEmpty("") = true; * isEmpty(" ") = false; * </pre> * * @param str * @return if string is null or its size is 0, return true, else return false. */ public static boolean isEmpty(CharSequence str) { return (str == null || str.length() == 0); } /** * compare two string * * @param actual * @param expected * @return * @see ObjectUtils#isEquals(Object, Object) */ public static boolean isEquals(String actual, String expected) { return ObjectUtils.isEquals(actual, expected); } /** * get length of CharSequence * <p> * <pre> * length(null) = 0; * length(\"\") = 0; * length(\"abc\") = 3; * </pre> * * @param str * @return if str is null or empty, return 0, else return {@link CharSequence#length()}. */ public static int length(CharSequence str) { return str == null ? 0 : str.length(); } /** * null Object to empty string * <p> * <pre> * nullStrToEmpty(null) = ""; * nullStrToEmpty("") = ""; * nullStrToEmpty("aa") = "aa"; * </pre> * * @param str * @return */ public static String nullStrToEmpty(Object str) { return (str == null ? "" : (str instanceof String ? (String) str : str.toString())); } /** * capitalize first letter * <p> * <pre> * capitalizeFirstLetter(null) = null; * capitalizeFirstLetter("") = ""; * capitalizeFirstLetter("2ab") = "2ab" * capitalizeFirstLetter("a") = "A" * capitalizeFirstLetter("ab") = "Ab" * capitalizeFirstLetter("Abc") = "Abc" * </pre> * * @param str * @return */ public static String capitalizeFirstLetter(String str) { if (isEmpty(str)) { return str; } char c = str.charAt(0); return (!Character.isLetter(c) || Character.isUpperCase(c)) ? str : new StringBuilder(str.length()) .append(Character.toUpperCase(c)).append(str.substring(1)).toString(); } /** * encoded in utf-8 * <p> * <pre> * utf8Encode(null) = null * utf8Encode("") = ""; * utf8Encode("aa") = "aa"; * utf8Encode("啊啊啊啊") = "%E5%95%8A%E5%95%8A%E5%95%8A%E5%95%8A"; * </pre> * * @param str * @return * @throws UnsupportedEncodingException if an error occurs */ public static String utf8Encode(String str) { if (!isEmpty(str) && str.getBytes().length != str.length()) { try { return URLEncoder.encode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UnsupportedEncodingException occurred. ", e); } } return str; } /** * encoded in utf-8, if exception, return defultReturn * * @param str * @param defultReturn * @return */ public static String utf8Encode(String str, String defultReturn) { if (!isEmpty(str) && str.getBytes().length != str.length()) { try { return URLEncoder.encode(str, "UTF-8"); } catch (UnsupportedEncodingException e) { return defultReturn; } } return str; } /** * get innerHtml from href * <p> * <pre> * getHrefInnerHtml(null) = "" * getHrefInnerHtml("") = "" * getHrefInnerHtml("mp3") = "mp3"; * getHrefInnerHtml("<a innerHtml</a>") = "<a innerHtml</a>"; * getHrefInnerHtml("<a>innerHtml</a>") = "innerHtml"; * getHrefInnerHtml("<a<a>innerHtml</a>") = "innerHtml"; * getHrefInnerHtml("<a href="baidu.com">innerHtml</a>") = "innerHtml"; * getHrefInnerHtml("<a href="baidu.com" title="baidu">innerHtml</a>") = "innerHtml"; * getHrefInnerHtml(" <a>innerHtml</a> ") = "innerHtml"; * getHrefInnerHtml("<a>innerHtml</a></a>") = "innerHtml"; * getHrefInnerHtml("jack<a>innerHtml</a></a>") = "innerHtml"; * getHrefInnerHtml("<a>innerHtml1</a><a>innerHtml2</a>") = "innerHtml2"; * </pre> * * @param href * @return <ul> * <li>if href is null, return ""</li> * <li>if not match regx, return source</li> * <li>return the last string that match regx</li> * </ul> */ public static String getHrefInnerHtml(String href) { if (isEmpty(href)) { return ""; } String hrefReg = ".*<[\\s]*a[\\s]*.*>(.+?)<[\\s]*/a[\\s]*>.*"; Pattern hrefPattern = Pattern.compile(hrefReg, Pattern.CASE_INSENSITIVE); Matcher hrefMatcher = hrefPattern.matcher(href); if (hrefMatcher.matches()) { return hrefMatcher.group(1); } return href; } /** * process special char in html * <p> * <pre> * htmlEscapeCharsToString(null) = null; * htmlEscapeCharsToString("") = ""; * htmlEscapeCharsToString("mp3") = "mp3"; * htmlEscapeCharsToString("mp3<") = "mp3<"; * htmlEscapeCharsToString("mp3>") = "mp3\>"; * htmlEscapeCharsToString("mp3&mp4") = "mp3&mp4"; * htmlEscapeCharsToString("mp3"mp4") = "mp3\"mp4"; * htmlEscapeCharsToString("mp3<>&"mp4") = "mp3\<\>&\"mp4"; * </pre> * * @param source * @return */ public static String htmlEscapeCharsToString(String source) { return StringUtils.isEmpty(source) ? source : source.replaceAll("<", "<").replaceAll(">", ">") .replaceAll("&", "&").replaceAll(""", "\""); } /** * transform half width char to full width char * <p> * <pre> * fullWidthToHalfWidth(null) = null; * fullWidthToHalfWidth("") = ""; * fullWidthToHalfWidth(new String(new char[] {12288})) = " "; * fullWidthToHalfWidth("!"#$%&) = "!\"#$%&"; * </pre> * * @param s * @return */ public static String fullWidthToHalfWidth(String s) { if (isEmpty(s)) { return s; } char[] source = s.toCharArray(); for (int i = 0; i < source.length; i++) { if (source[i] == 12288) { source[i] = ' '; // } else if (source[i] == 12290) { // source[i] = '.'; } else if (source[i] >= 65281 && source[i] <= 65374) { source[i] = (char) (source[i] - 65248); } else { source[i] = source[i]; } } return new String(source); } /** * transform full width char to half width char * <p> * <pre> * halfWidthToFullWidth(null) = null; * halfWidthToFullWidth("") = ""; * halfWidthToFullWidth(" ") = new String(new char[] {12288}); * halfWidthToFullWidth("!\"#$%&) = "!"#$%&"; * </pre> * * @param s * @return */ public static String halfWidthToFullWidth(String s) { if (isEmpty(s)) { return s; } char[] source = s.toCharArray(); for (int i = 0; i < source.length; i++) { if (source[i] == ' ') { source[i] = (char) 12288; // } else if (source[i] == '.') { // source[i] = (char)12290; } else if (source[i] >= 33 && source[i] <= 126) { source[i] = (char) (source[i] + 65248); } else { source[i] = source[i]; } } return new String(source); } }
总结,错误的原因多次绑定文本变化监听器。此处的解决方案是,已经绑定监听器的,就先移除重新绑定。还有一种思路是禁用recycleview的复用性holder.setIsRecyclable(false) ,可以考虑使用,但当item数量比较多的时候,不建议使用,禁用复用性,就失去了recycleView的价值;