英文:
Masking inputted text using canvas on android kotlin
问题
我目前正在进行自定义的PIN码功能,使用了Canvas和Paint来实现。不幸的是,我似乎无法复制`inputType="numberPassword"`所做的事情,并应用于我的自定义设计。我希望在将数字遮盖并继续输入下一个PIN码之前,暂时显示数字。
以下是我的CustomEditTextPinView类的代码:
```kotlin
class CustomEditTextPinViewMask : AppCompatEditText {
// 构造函数和其他变量的初始化...
override fun onDraw(canvas: Canvas) {
// super.onDraw(canvas) // 注释掉这一行
// 绘制PIN码的逻辑...
}
}
这是如何在XML布局中使用的示例:
<com.sample.ui.widget.CustomEditTextPinViewMask
android:id="@+id/pv_pincode"
android:layout_width="260dp"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:inputType="numberPassword"
android:letterSpacing="1"
android:maxLength="6"
android:textColor="@color/text_label_black"
android:textIsSelectable="false"
android:textSize="28sp"/>
这是在手机上显示的外观示例:
如果你想要遮盖PIN码,你可以在CustomEditTextPinViewMask类的onDraw
方法中的相应位置添加遮盖逻辑。一种常见的做法是使用圆点(•)或其他遮盖字符来代替实际的数字。
希望这对你有帮助!如果你有其他问题或建议,欢迎继续提问。```
英文:
I'm currently doing a custom pin code and doing it with a canvas and paint, Unfortunately I cant seems to copy what the iputType="numberPassword" is doing with my custom design, I want to show the number momentarily before masking it and proceed to the next pin.
Here is my customEditText
class CustomEditTextPinViewMask : AppCompatEditText {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs!!)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(context, attrs!!)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr)
val XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android"
private var mSpace = 24f//24 dp by default, space between the lines
private var mCharSize = 0f
private var mNumChars = 6f
private var mLineSpacing = 8f //8dp by default, height of the text from our lines
private var mMaxLength = 6
private var mClickListener: View.OnClickListener? = null
private var mLineStroke = 1f//1dp by default
private var mLineStrokeSelected = 2f//2dp by default
private var mLinesPaint: Paint? = null
private var mLinesGone: Paint? = null
private var mPaint: Paint? = null
val mStates = arrayOf(
intArrayOf(R.attr.state_selected),
intArrayOf(R.attr.state_focused),
intArrayOf(-R.attr.state_focused)
)
val mColors = intArrayOf(
-0x493800,//Green color = 0xFFB6C800
-0x333334,//Gray color = 0xFFCCCCCC
-0x333334
)
val mColorStates = ColorStateList(mStates, mColors)
private fun init(context: Context, attrs: AttributeSet) {
val multi = context.resources.displayMetrics.density
mLineStroke *= multi
mLineStrokeSelected *= multi
mLinesPaint = Paint(paint)
mLinesPaint!!.strokeWidth = mLineStroke
mLinesPaint!!.color = Color.parseColor("#969696")
mLinesGone = Paint(paint)
mLinesGone!!.strokeWidth = mLineStroke
mLinesGone!!.color = Color.parseColor("#FFFFFF")
mPaint = Paint(paint)
setBackgroundResource(0)
mSpace *= multi //convert to pixels for our density
mLineSpacing *= multi //convert to pixels for our density
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 6)
mNumChars = mMaxLength.toFloat()
//Disable copy paste
super.setCustomSelectionActionModeCallback(object : ActionMode.Callback {
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode) {}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
return false
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return false
}
})
// When tapped, move cursor to end of text.
super.setOnClickListener(View.OnClickListener { v ->
setSelection(text!!.length)
if (mClickListener != null) {
mClickListener!!.onClick(v)
}
})
}
override fun setOnClickListener(l: View.OnClickListener?) {
mClickListener = l
}
override fun setCustomSelectionActionModeCallback(actionModeCallback: ActionMode.Callback?) {
throw RuntimeException("setCustomSelectionActionModeCallback() not supported.")
}
private fun getColorForState(vararg states: Int): Int {
return mColorStates.getColorForState(states, Color.GRAY)
}
private fun updateColorForLines(next: Boolean) {
if (isFocused) {
mLinesPaint!!.strokeWidth = mLineStrokeSelected
mLinesPaint!!.color = getColorForState(R.attr.state_focused)
mLinesPaint!!.color = getColorForState(R.attr.state_window_focused)
if (next) {
mLinesPaint!!.color = getColorForState(R.attr.state_selected)
}
} else {
mLinesPaint!!.strokeWidth = mLineStroke
mLinesPaint!!.color = getColorForState(-R.attr.state_focused)
}
}
override fun onDraw(canvas: Canvas) {
// super.onDraw(canvas)
val availableWidth = width - paddingRight - paddingLeft
if (mSpace < 0) {
mCharSize = availableWidth / (mNumChars * 2 - 1)
} else {
mCharSize = (availableWidth - mSpace * (mNumChars - 1)) / mNumChars
}
var startX = paddingLeft
val bottom = height - paddingBottom
//Text Width
val text: Editable? = text
val textLength = text?.length
val textWidths = FloatArray(textLength!!)
paint.getTextWidths(getText(), 0, textLength, textWidths)
//If Wanted to change text Color
var i = 0
while (i < mNumChars) {
updateColorForLines(i == textLength)
canvas.drawLine(
startX.toFloat(), bottom.toFloat(), (startX + mCharSize) + letterSpacing,
bottom.toFloat(), mLinesPaint!!
)
if (getText()!!.length > i) {
val middle = startX + mCharSize / 2
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2,
bottom - mLineSpacing, paint)
mLinesGone!!.strokeWidth = mLineStrokeSelected
canvas.drawLine(
startX.toFloat(), bottom.toFloat(),
startX + mCharSize, bottom.toFloat(), mLinesGone!!
)
canvas.drawLine(
startX.toFloat(), bottom.toFloat(),
startX + mCharSize, bottom.toFloat(), mLinesGone!!
)
canvas.drawLine(
startX.toFloat(), bottom.toFloat(),
startX + mCharSize, bottom.toFloat(), mLinesGone!!
)
}
if (mSpace < 0) {
var mCharSizeMult = mCharSize * 2
startX = (startX + mCharSizeMult).toInt()
} else {
var mCharSizeMspace = mCharSize + mSpace.toInt()
startX = (startX + mCharSizeMspace).toInt()
}
i++
}
}
}
This is how i implement it on my xml
<com.sample.ui.widget.CustomEditTextPinViewMask
android:id="@+id/pv_pincode"
android:layout_width="260dp"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:inputType="numberPassword"
android:letterSpacing="1"
android:maxLength="6"
android:textColor="@color/text_label_black"
android:textIsSelectable="false"
android:textSize="28sp"/>
This is how it will look like when shown on the phone
How can i mask it? I'm also open for any sugestions thank you.
答案1
得分: 0
只需使用**setTransformationMethod()** 进行掩盖。
您可以使用此方法对编辑框进行掩盖。
// 默认情况下
edittext.setTransformationMethod(new PasswordTransformationMethod());
// 您可以自定义
edittext.setTransformationMethod(new AsterPasswordTransformationMethod());
下面是AsterPasswordTransformationMethod类的代码示例:
public class AsterPasswordTransformationMethod extends PasswordTransformationMethod {
@Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // 存储字符序列
}
public char charAt(int index) {
return '*'; // 这是关键部分
}
public int length() {
return mSource.length(); // 返回默认值
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // 返回默认值
}
}
};
英文:
Just use setTransformationMethod() for masking it.
You can use this to mask the edittext .
//by default
edittext.setTransformationMethod(new PasswordTransformationMethod());
//You can make it custom
edittext.setTransformationMethod(new AsterPasswordTransformationMethod());
here is the AsterPasswordTransformationMethod class
public class AsterPasswordTransformationMethod extends PasswordTransformationMethod {
@Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // Store char sequence
}
public char charAt(int index) {
return '*'; // This is the important part
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
};
专注分享java语言的经验与见解,让所有开发者获益!
评论