【问题标题】:Visible password with TextInputLayouts passwordToggleEnabled可见密码与 TextInputLayouts passwordToggleEnabled
【发布时间】:2017-05-27 11:02:15
【问题描述】:

我正在使用带有支持库中的新函数的 TextInputLayout:passwordToggleEnabled。这提供了一个漂亮的“眼睛”图标,让用户可以打开和关闭密码可见性。

我的问题是,是否有办法使用此功能但从密码可见开始?

我的 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);显示了密码,但切换开关消失了,用户无法再次隐藏密码。我知道我可以手动完成所有操作,但我只是想知道我是否可以使用新的魔法切换来让它工作

【问题讨论】:

  • 你可以查看这个答案stackoverflow.com/a/27050480/7329597希望它对你有帮助:)
  • 你必须检查这个答案stackoverflow.com/a/27050480/7329597希望它有帮助。:)
  • @SejalBell - 这是用于手动显示或隐藏密码。但它不适用于 TextInputLayouts 新的自动切换。如果我按照答案建议将输入类型设置为可见密码,则切换按钮消失了。我想将密码显示为默认密码,但允许用户再次隐藏它。
  • 我也在寻找一种方法来实现这一点,而不是尝试直接从 AOSP 扩展类
  • 嗨,我正在努力实现同样的目标。你知道怎么做吗?

标签: android android-textinputlayout


【解决方案1】:

最简单的方法如下

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();
        }
    });
}

现在让我解释一下答案

在查看TextInputLayout.java 的代码时,我发现design_text_input_password_icon.xml 的布局正在添加到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&lt;T&gt; 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;
}

【讨论】:

  • 对您的第一个解决方案的一些评论 - R.id.text_input_password_toggle 不是公共 ID。
  • @CheokYanCheng 解决方法我前几天发的找到视图不使用id,而是使用内容描述
  • @CheokYanCheng 我没有得到 public-id 的概念。如果您谈论的是在 Android 本身创建的布局中声明的 id,那么我会说如果您知道使用该 id 是可以的,并且您的解决方案将一直有效,直到他们不更改 id。这类似于旧样式​​的 listview 示例,其中我们提供了 android 声明的 listview 的 id。并且有很多我们使用此类 ID 的示例。但是如果我没有得到它,你能解释一下 public-id 吗?
  • 在 2019 年,TextInputLayout 上有一个名为 passwordVisibilityToggleRequested() 的公共方法可以为您执行此操作。
【解决方案2】:

使用材料组件库(1.1.01.2.0-beta011.3.0-alpha01),只需使用可见密码即可:

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

在你的代码中:

textInputLayout.getEditText().setTransformationMethod(null);

如果要恢复默认行为:

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

【讨论】:

    【解决方案3】:

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

    这是代码 sn-p。

    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();
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      如果他们为 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();
              }
          }
      }
      

      【讨论】:

      • 在 2019 年,TextInputLayout 上有一个名为 passwordVisibilityToggleRequested() 的公共方法可以为您执行此操作。
      • passwordVisibilityToggleRequested 已弃用
      【解决方案5】:

      试试这个

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

      【讨论】:

        【解决方案6】:

        您可以使用下面的代码:

        TextInputLayout yourTextInputLayoutId = findViewById(R.id.yourTextInputLayoutId);
        FrameLayout frameLayout = (FrameLayout) (yourTextInputLayoutId).getChildAt(0);
        
        CheckableImageButton checkableImageButton = (CheckableImageButton) frameLayout.getChildAt(1);
        checkableImageButton.performClick();
        

        这里yourTextInputLayoutId 是您来自 xml 的 TextInputLayout id。

        【讨论】:

          【解决方案7】:

          从密码可见开始, 不包括

          android:inputType="textPassword"
          

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

          【讨论】:

          • 我没有给你投反对票,我只是编辑了你帖子中的代码标签
          • 好的,但是你检查过它是否有效吗?
          • 我没有;我没有遇到这个问题,所以我无法在这里检查答案的有效性。也许问问 OP,看看你的建议是否适用于他们的情况,以及他们是否从你的答案中受益比从接受的答案中受益更多。
          • 不错的答案亲
          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            你可以使用:

            yourEditText.setTransformationMethod(new PasswordTransformationMethod());
            

            要重新显示可读密码,只需传递 null 作为转换方法:

            yourEditText.setTransformationMethod(null);
            

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

            【讨论】:

            • 感谢您的回答,但这也会使切换按钮消失。我已经扩展了我的问题,以明确什么不起作用。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-22
            • 2015-07-06
            • 2022-01-01
            • 2020-03-29
            相关资源
            最近更新 更多