这是一篇记叙文
记叙文六要素:人物、时间、地点、事件发生的起因、经过、结果
人物:
保密
时间:
2017-06-28
地点:
保密
- 上来就贴代码
<android.support.design.widget.TextInputLayout
android:id="@+id/til_new_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="5"
android:gravity="center_vertical"
android:hint="提示的话"
app:counterEnabled="true"
app:counterMaxLength="10"
app:counterOverflowTextAppearance="@style/TextAppearance.Design.Counter.Overflow"
app:counterTextAppearance="@style/TextAppearance.Design.Counter">
<android.support.design.widget.TextInputEditText
android:id="@+id/et_new_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits="0123456789ABCDEFabcdef"
android:maxLength="10"
android:singleLine="true"
android:textSize="24sp"
tools:text="00FF010206"/>
</android.support.design.widget.TextInputLayout>
- 对应的图图
TextInputLayout继承自LinearLayout,TextInputEditText继承自AppCompatEditText,二者结合使用就是为了实现上图的风骚效果的。需要依赖Design包
compile 'com.android.support:design:25.3.1'
起因:
我想通过继承TextInputEditText自定义一个TadIDEditText,在上图风骚效果的基础上实现TagID号(eg:0102030405)每两位后面自动加一个空格(Space)的效果,代码如下
public class TadIDEditText extends TextInputEditText {
private boolean shouldStopChange = false;
public TadIDEditText(Context context) {
super(context);
}
public TadIDEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TadIDEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private TextWatcher mTagIDEidtTextWatcher = 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) {
}
@Override
public void afterTextChanged(Editable s) {
if (shouldStopChange) {
shouldStopChange = false;
return;
}
shouldStopChange = true;
String str = s.toString().trim().replaceAll(" ", "");
StringBuffer sb = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
sb.append(str.charAt(i));
if (i % 2 == 1 && i != str.length() - 1) {
sb.append(" ");
}
}
setText(sb);
setSelection(sb.length());
}
};
@Override
public void addTextChangedListener(TextWatcher watcher) {
super.addTextChangedListener(mTagIDEidtTextWatcher);
}
@Override
public void removeTextChangedListener(TextWatcher watcher) {
super.removeTextChangedListener(mTagIDEidtTextWatcher);
}
public String getInputTagIDText() {
return getText().toString().replace(" ", "").trim();
}
public TextWatcher getTagIDEditTextWatcher() {
return mTagIDEidtTextWatcher;
}
}
经过:
- 先说明一下shouldStopChange的作用。
因为是在afterTextChanged方法中判断、添加空格的,所以执行完afterTextChanged方法后,EditText的Text又变了,又会触发TextChanged,如果不通过这个变量控制,就会进入死循环,界面卡死,方法栈溢出。 - addTextChangedListener(TextWatcher watcher)的源码(在TextView中)如下
public void addTextChangedListener(TextWatcher watcher) {
if (mListeners == null) {
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
}
void sendAfterTextChanged(Editable text) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
for (int i = 0; i < count; i++) {
list.get(i).afterTextChanged(text);
}
}
hideErrorIfUnchanged();
}
也就是你可以给TextView及其继承者们添加多个TextWatcher,当其中的Text发生变化时,会依次调用这些TextWatcher中的几个方法。
注意:依次、依次、依次。
- 为了实现良好的封装,我希望添加空格的逻辑写在TadIDEditText中,鉴于此,我写好了一个mTagIDEidtTextWatcher,为了让我这个EditText功能专一,我重写了
addTextChangedListener
,如下,就是让它只能设置这一个监听器。
@Override
public void addTextChangedListener(TextWatcher watcher) {
super.addTextChangedListener(mTagIDEidtTextWatcher);
}
结果造成了下面的问题
使用TadIDEditText的时候,多次调用addTextChangedListener会造成多次添加mTagIDEidtTextWatcher,比如添加了2次,依据上面的分析我shouldStopChange这个变量就失去作用了,照样造成死循环、方法栈溢出。
我在Activity中调用了一次addTextChangedListener,结果还是界面卡死、方法栈溢出了。然后通过一个简单的log,我发现尽管我只调用了一次,但是addTextChangedListener执行了两次,而且即使我不调用,addTextChangedListener也会执行一次。这就奇怪了???而且TextInputLayout右下角的计数也不计数了,这下就明白了,TextInputLayout和TextInput搭配使用,之所以能计数,不就是加了一个TextWatcher吗?看源码,果不其然。终于在TextInputLayout中发现了问题所在。以下贴出TextInputLayout中的几个方法。
@Override
public void addView(View child, int index, final ViewGroup.LayoutParams params) {
if (child instanceof EditText) {
// Make sure that the EditText is vertically at the bottom, so that it sits on the
// EditText's underline
FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(params);
flp.gravity = Gravity.CENTER_VERTICAL | (flp.gravity & ~Gravity.VERTICAL_GRAVITY_MASK);
mInputFrame.addView(child, flp);
// Now use the EditText's LayoutParams as our own and update them to make enough space
// for the label
mInputFrame.setLayoutParams(params);
updateInputLayoutMargins();
setEditText((EditText) child);<<<<<<<<<<<<<<<<<<<重点看这里
} else {
// Carry on adding the View...
super.addView(child, index, params);
}
}
private void setEditText(EditText editText) {
// If we already have an EditText, throw an exception
......
// Add a TextWatcher so that we know when the text input has changed
mEditText.addTextChangedListener(new TextWatcher() {<<<<<<<<<<<<<<<<<<<重点看这里
@Override
public void afterTextChanged(Editable s) {
updateLabelState(!mRestoringSavedState);
if (mCounterEnabled) {
updateCounter(s.length());//更新计数
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
// Use the EditText's hint colors if we don't have one set
......
}
看到这儿总算找出了我的bug,看来我这种写法问题多多啊,不过也通过问题对这两个控件了解了一下。
结果:
我想想还能怎么改。继续撸码。