带有 TextInputLayouts passwordToggleEnabled 的可见密码

Visible password with TextInputLayouts passwordToggleEnabled

我正在使用带有支持库中的新功能的 TextInputLayout:passwordToggleEnabled。这提供了一个漂亮的 "eye" 图标,允许用户打开和关闭密码可见性。

我的问题是是否有一种方法可以使用此功能但从密码可见开始?

我的xml:

<android.support.design.widget.TextInputLayout
                    android:id="@+id/password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:passwordToggleEnabled="true">

                    <EditText
                        android:id="@+id/password_edit"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="@string/prompt_password"
                        android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout>

切换看起来类似于:

我在 xml 中没有找到执行此操作的方法,也没有在呈现视图后手动切换可见性的方法。如果我将 EditText 的输入类型设置为 textVisiblePassword,则不会显示切换。如果我在代码中使用 mPasswordEditText.setTransformationMethod(null);显示了密码,但切换消失了,用户无法再次隐藏密码。我知道我可以手动完成所有操作,但只是想知道我是否可以使用新的魔术切换

您可以使用:

yourEditText.setTransformationMethod(new PasswordTransformationMethod());

到re-show可读密码,只需要传递null作为转换方法:

yourEditText.setTransformationMethod(null);

所以用户可以再次隐藏它。

其中一种方法是,我们可以从 TextInputLayout 中搜索 CheckableImageButton,然后根据 EditText 的密码可见性状态,以编程方式对其执行 onClick .

这是代码片段。

private CheckableImageButton findCheckableImageButton(View view) {
    if (view instanceof CheckableImageButton) {
        return (CheckableImageButton)view;
    }

    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = (ViewGroup) view;
        for (int i = 0, ei = viewGroup.getChildCount(); i < ei; i++) {
            CheckableImageButton checkableImageButton = findCheckableImageButton(viewGroup.getChildAt(i));
            if (checkableImageButton != null) {
                return checkableImageButton;
            }
        }
    }

    return null;
}

//...

if (passwordEditText.getTransformationMethod() != null) {
    CheckableImageButton checkableImageButton = findCheckableImageButton(passwordTextInputLayout);
    if (checkableImageButton != null) {
        // Make password visible.
        checkableImageButton.performClick();
    }
}

我能够使用以下代码让它以明文模式启动。基本上,我必须使用内容描述找到正确的视图。

如果他们为 mPasswordToggledVisibility 提供了一个 setter 方法,那会让事情变得容易很多...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TextInputLayout til = findViewById(R.id.password);
    CharSequence cs = til.getPasswordVisibilityToggleContentDescription();
    ArrayList<View> ov = new ArrayList<>();
    til.findViewsWithText(ov, cs,View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
    if( ov.size() == 1 ) {
        Checkable c = (Checkable)ov.get(0);
        // As far as I can tell the check for "isChecked" here isn't needed,
        // since it always starts unchecked by default. However, if you
        // wanted to check for state, you could do it this way.
        if( c != null && !c.isChecked()) {
            ov.get(0).performClick();
        }
    }
}

下面是最简单的方法 这个答案的最后是另一个解决方案

private void setupPasswordToggleView() {
    final TextInputLayout textInputLayout = mRootView.findViewById(R.id.password);

    // You can skip post-call and write directly the code which is inside run method.
    // But to be safe (as toggle-view is child of TextInputLayout, post call
    // has been added.
    textInputLayout.post(new Runnable() {
        @Override
        public void run() {
            CheckableImageButton passwordToggleView = textInputLayout.findViewById(R.id.text_input_password_toggle);
            // passwordToggleView.toggle(); // Can not use as restricted to use same library group
            // passwordToggleView.setChecked(true); // Can not use as restricted to use same library group
            passwordToggleView.performClick();
        }
    });
}

Now let me explain the answer

在查看 TextInputLayout.java I found that, there is a layout design_text_input_password_icon.xml which is being added to TextInputLayout.java 的代码时。下面是代码

private void updatePasswordToggleView() {
    if (mEditText == null) {
        // If there is no EditText, there is nothing to update
        return;
    }
    if (shouldShowPasswordIcon()) {
        if (mPasswordToggleView == null) {
            mPasswordToggleView = (CheckableImageButton) LayoutInflater.from(getContext())
                    .inflate(R.layout.design_text_input_password_icon, mInputFrame, false);
            mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
            mPasswordToggleView.setContentDescription(mPasswordToggleContentDesc);
            mInputFrame.addView(mPasswordToggleView); // << HERE IS THAT
            .........
}

现在下一个目标是找到 design_text_input_password_icon.xml 和切换视图的查找 ID。所以找到布局 design_text_input_password_icon.xml here 并写成

18<android.support.design.widget.CheckableImageButton
19    xmlns:android="http://schemas.android.com/apk/res/android"
20    android:id="@+id/text_input_password_toggle"
21    android:layout_width="wrap_content"
22    android:layout_height="wrap_content"
23    android:layout_gravity="center_vertical|end|right"
24    android:background="?attr/selectableItemBackgroundBorderless"
25    android:minHeight="48dp"
26    android:minWidth="48dp"/>

我找到了那个视图的 ID text_input_password_toggle,现在一切都很容易,只需在它的视图组中找到那个视图并对其执行操作。


另一种解决方案是迭代 TextInputLayout 的子项并检查它是否为 CheckableImageButton 然后执行单击它。通过这种方式,不会依赖于该视图的 id,如果 Android 更改了视图的 id,我们的解决方案仍然有效。 (尽管在正常情况下它们不会更改视图的 id)。

private void setupPasswordToggleViewMethod2() {
    final TextInputLayout textInputLayout = mRootView.findViewById(R.id.password);

    textInputLayout.post(new Runnable() {
        @Override
        public void run() {

            View toggleView = findViewByClassReference(textInputLayout, CheckableImageButton.class);
            if (toggleView != null) {
                toggleView.performClick();
            }
        }
    });
}

其中findViewByClassReference(View rootView, Class<T> clazz)original utility class定义如下

public static <T extends View> T findViewByClassReference(View rootView, Class<T> clazz) {
    if(clazz.isInstance(rootView)) {
        return clazz.cast(rootView);
    }
    if(rootView instanceof ViewGroup) {
        ViewGroup viewGroup = (ViewGroup) rootView;
        for(int i = 0; i < viewGroup.getChildCount(); i++) {
            View child = viewGroup.getChildAt(i);
            T match = findViewByClassReference(child, clazz);
            if(match != null) {
                return match;
            }
        }
    }
    return null;
}

您可以使用以下代码:

TextInputLayout yourTextInputLayoutId = findViewById(R.id.yourTextInputLayoutId);
FrameLayout frameLayout = (FrameLayout) (yourTextInputLayoutId).getChildAt(0);

CheckableImageButton checkableImageButton = (CheckableImageButton) frameLayout.getChildAt(1);
checkableImageButton.performClick();

这里 yourTextInputLayoutId 是来自 xml 的 TextInputLayout id。

试试这个

if (inputEditText.getTransformationMethod() == null) {
    inputEditText.setTransformationMethod(new PasswordTransformationMethod());
} else {
    inputEditText.setTransformationMethod(null);
}


inputEditText.setSelection(inputEditText.getText().length());

要从密码可见开始, 不包括

android:inputType="textPassword"

<com.google.android.material.textfield.TextInputEditText>
.... 

</com.google.android.material.textfield.TextInputEditText>

使用 Material 组件库(1.1.01.2.0-beta011.3.0-alpha01)以可见密码开始,只需使用:

<com.google.android.material.textfield.TextInputLayout
    app:endIconMode="password_toggle"
/>

并在您的代码中:

textInputLayout.getEditText().setTransformationMethod(null);

如果您想 return 默认行为:

textInputLayout.getEditText()
    .setTransformationMethod(PasswordTransformationMethod.getInstance());

只是删除 android:inputType="textPassword" 对我有用