在做一个项目的时候客户要求密码框显示*号,这个纠结了一下啊,之前想做监听,但是发现bug好多,仔细的看了源代码终于找出解决方案,setTransformationMethod(new PasswordTransformationMethod());就可以达到想要的效果,源代码中的...其实是写死到代码中了,我们没法改,所以只能重写PasswordTransformationMethod,关于这个类里面包含内容比较多,值得注意的是每输入一个字符要延时一下才变成*,这里其实用handler做的处理。今天刚做出来,和大家分享一下,欢迎前辈批评指正。(用的时候在xml中加载该类(AsteriskPasswordEditText))
public class AsteriskPasswordEditText extends EditText
{
public AsteriskPasswordEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
this.setTransformationMethod(new PasswordTransformationMethod());
}
static class PasswordTransformationMethod implements TransformationMethod,
TextWatcher
{
static final Object ACTIVE = new NoCopySpan.Concrete();
private WeakReference<ContentResolver> mResolver;
private boolean mPrefsInited;
/* package */static final int SHOW_PASSWORD = 8;
/* package */static final int AUTO_TEXT = 2;
/* package */static final int AUTO_PERIOD = 4;
/* package */static final int AUTO_CAP = 1;
private int mPrefs;
private SettingsObserver mObserver;
public CharSequence getTransformation(CharSequence source, View view)
{
if (source instanceof Spannable)
{
Spannable sp = (Spannable) source;
/*
* Remove any references to other views that may still be
* attached. This will happen when you flip the screen
* while a password field is showing; there will still
* be references to the old EditText in the text.
*/
ViewReference[] vr = sp.getSpans(0,
sp.length(),
ViewReference.class);
for (int i = 0; i < vr.length; i++)
{
sp.removeSpan(vr[i]);
}
removeVisibleSpans(sp);
sp.setSpan(new ViewReference(view),
0,
0,
Spannable.SPAN_POINT_POINT);
}
return new PasswordCharSequence(source);
}
public static PasswordTransformationMethod getInstance()
{
if (sInstance != null)
return sInstance;
sInstance = new PasswordTransformationMethod();
return sInstance;
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
{
// This callback isn't used.
}
public void onTextChanged(CharSequence s, int start, int before,
int count)
{
if (s instanceof Spannable)
{
Spannable sp = (Spannable) s;
ViewReference[] vr = sp.getSpans(0,
s.length(),
ViewReference.class);
if (vr.length == 0)
{
return;
}
/*
* There should generally only be one ViewReference in the text,
* but make sure to look through all of them if necessary in case
* something strange is going on. (We might still end up with
* multiple ViewReferences if someone moves text from one password
* field to another.)
*/
View v = null;
for (int i = 0; v == null && i < vr.length; i++)
{
v = vr[i].get();
}
if (v == null)
{
return;
}
int pref = getPrefs(v.getContext());
if ((pref & SHOW_PASSWORD) != 0)
{
if (count > 0)
{
removeVisibleSpans(sp);
if (count == 1)
{
sp.setSpan(new Visible(sp, this), start, start
+ count, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
}
//TextKeyListener������ start==================================================================
/* package */int getPrefs(Context context)
{
synchronized (this)
{
if (!mPrefsInited || mResolver.get() == null)
{
initPrefs(context);
}
}
return mPrefs;
}
private void initPrefs(Context context)
{
final ContentResolver contentResolver = context.getContentResolver();
mResolver = new WeakReference<ContentResolver>(contentResolver);
if (mObserver == null)
{
mObserver = new SettingsObserver();
contentResolver.registerContentObserver(Settings.System.CONTENT_URI,
true,
mObserver);
}
updatePrefs(contentResolver);
mPrefsInited = true;
}
private class SettingsObserver extends ContentObserver
{
public SettingsObserver()
{
super(new Handler());
}
@Override
public void onChange(boolean selfChange)
{
if (mResolver != null)
{
final ContentResolver contentResolver = mResolver.get();
if (contentResolver == null)
{
mPrefsInited = false;
}
else
{
updatePrefs(contentResolver);
}
}
else
{
mPrefsInited = false;
}
}
}
private void updatePrefs(ContentResolver resolver)
{
boolean cap = System.getInt(resolver, System.TEXT_AUTO_CAPS, 1) > 0;
boolean text = System.getInt(resolver, System.TEXT_AUTO_REPLACE, 1) > 0;
boolean period = System.getInt(resolver,
System.TEXT_AUTO_PUNCTUATE,
1) > 0;
boolean pw = System.getInt(resolver, System.TEXT_SHOW_PASSWORD, 1) > 0;
mPrefs = (cap ? AUTO_CAP : 0) | (text ? AUTO_TEXT : 0)
| (period ? AUTO_PERIOD : 0) | (pw ? SHOW_PASSWORD : 0);
}
//TextKeyListener������ end==================================================================
public void afterTextChanged(Editable s)
{
}
public void onFocusChanged(View view, CharSequence sourceText,
boolean focused, int direction, Rect previouslyFocusedRect)
{
if (!focused)
{
if (sourceText instanceof Spannable)
{
Spannable sp = (Spannable) sourceText;
removeVisibleSpans(sp);
}
}
}
private static void removeVisibleSpans(Spannable sp)
{
Visible[] old = sp.getSpans(0, sp.length(), Visible.class);
for (int i = 0; i < old.length; i++)
{
sp.removeSpan(old[i]);
}
}
private static class PasswordCharSequence implements CharSequence,
GetChars
{
public PasswordCharSequence(CharSequence source)
{
mSource = source;
}
public int length()
{
return mSource.length();
}
public char charAt(int i)
{
if (mSource instanceof Spanned)
{
Spanned sp = (Spanned) mSource;
int st = sp.getSpanStart(ACTIVE);
int en = sp.getSpanEnd(ACTIVE);
if (i >= st && i < en)
{
return mSource.charAt(i);
}
Visible[] visible = sp.getSpans(0,
sp.length(),
Visible.class);
for (int a = 0; a < visible.length; a++)
{
if (sp.getSpanStart(visible[a].mTransformer) >= 0)
{
st = sp.getSpanStart(visible[a]);
en = sp.getSpanEnd(visible[a]);
if (i >= st && i < en)
{
return mSource.charAt(i);
}
}
}
}
return DOT;
}
public CharSequence subSequence(int start, int end)
{
char[] buf = new char[end - start];
getChars(start, end, buf, 0);
return new String(buf);
}
public String toString()
{
return subSequence(0, length()).toString();
}
public void getChars(int start, int end, char[] dest, int off)
{
TextUtils.getChars(mSource, start, end, dest, off);
int st = -1, en = -1;
int nvisible = 0;
int[] starts = null, ends = null;
if (mSource instanceof Spanned)
{
Spanned sp = (Spanned) mSource;
st = sp.getSpanStart(ACTIVE);
en = sp.getSpanEnd(ACTIVE);
Visible[] visible = sp.getSpans(0,
sp.length(),
Visible.class);
nvisible = visible.length;
starts = new int[nvisible];
ends = new int[nvisible];
for (int i = 0; i < nvisible; i++)
{
if (sp.getSpanStart(visible[i].mTransformer) >= 0)
{
starts[i] = sp.getSpanStart(visible[i]);
ends[i] = sp.getSpanEnd(visible[i]);
}
}
}
for (int i = start; i < end; i++)
{
if (!(i >= st && i < en))
{
boolean visible = false;
for (int a = 0; a < nvisible; a++)
{
if (i >= starts[a] && i < ends[a])
{
visible = true;
break;
}
}
if (!visible)
{
dest[i - start + off] = DOT;
}
}
}
}
private CharSequence mSource;
}
/**
* ��������ϢתΪ* ��Ĭ����ʱ1.5�룩
*/
private static class Visible extends Handler implements UpdateLayout,
Runnable
{
public Visible(Spannable sp, PasswordTransformationMethod ptm)
{
mText = sp;
mTransformer = ptm;
postAtTime(this, SystemClock.uptimeMillis() + 1500);
}
public void run()
{
mText.removeSpan(this);
}
private Spannable mText;
private PasswordTransformationMethod mTransformer;
}
/**
* Used to stash a reference back to the View in the Editable so we
* can use it to check the settings.
*/
private static class ViewReference extends WeakReference<View>
implements NoCopySpan
{
public ViewReference(View v)
{
super(v);
}
}
private static PasswordTransformationMethod sInstance;
private static char DOT = '*';
}
}