IOS style password control on Android

A while ago I came across a kind of weird requirement by an IOS biased client :P, he wanted to have a control which looks like an IOS lock screen password field.

                                     VS246

Since there is no control available in Android of this kind, I decided to create a compound component using a hidden EditText underneath other layouts.

Compound component is a custom FrameLayout with the following XML ui:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"   
   android:layout_width="match_parent"   
   android:layout_height="match_parent"   
   android:layout_margin="3dp"   
   android:layout_marginLeft="10dp" >   
   <EditText   
    android:id="@+id/edit_text"   
    android:layout_width="match_parent"   
    android:layout_height="match_parent"   
    android:layout_margin="3dp"   
    android:background="@android:color/transparent"   
    android:cursorVisible="false"   
    android:maxLength="6" />   
   <View   
    android:id="@+id/dotLayoutCoverBg"   
    android:layout_width="match_parent"   
    android:layout_height="match_parent"   
    android:background="@android:color/white" />   
   <LinearLayout   
    android:id="@+id/dottedPwdLayout"   
    android:layout_width="match_parent"   
    android:layout_height="match_parent"   
    android:baselineAligned="false"   
    android:gravity="center"   
    android:orientation="horizontal" >   
   </LinearLayout>   
  </FrameLayout>  

FrameLayout has below elements :

  • An Edittext, which is our main password field
  • View with a background colour(same as page bg) to cover Edittext and make it hidden to user.
  • A LinearLayout to which we add children layouts with a dot view based on max no.of password characters allowed.
  • A child layout is another LinearLayout with a ‘Square'(can be customised as per your UI needs) background and a view with dot shaped background.

Here are shapes that we use in this application:

– sqaure.xml & dot.xml

 <?xml version="1.0" encoding="utf-8"?>   
  <shape xmlns:android="http://schemas.android.com/apk/res/android"   
   android:shape="rectangle" >   
   <solid android:color="#ede3e7" />   
  </shape>   
  <?xml version="1.0" encoding="utf-8"?>   
  <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >   
   <solid android:color="#000000"/>   
  </shape>   

Source for Compound component looks like

 import com.sample.sri.iphonestyelpasswrod.R;   
  import android.content.Context;   
  import android.content.res.TypedArray;   
  import android.text.Editable;   
  import android.text.TextWatcher;   
  import android.util.AttributeSet;   
  import android.util.Log;   
  import android.view.LayoutInflater;   
  import android.view.View;   
  import android.view.View.OnFocusChangeListener;   
  import android.widget.EditText;   
  import android.widget.FrameLayout;   
  import android.widget.LinearLayout;   
  /**   
  *    
  * @author Sri - (srihari.yachamaneni@gmail.com)   
  *    
  */   
  public class DottedPasswordLayout extends FrameLayout implements TextWatcher,   
       OnFocusChangeListener {   
    private EditText edit_text;   
    private LinearLayout dotLayout;   
    /*   
     * Max no.of chars allowed, default is 4   
     */   
    private int dottedChildCount = 4;   
    public DottedPasswordLayout(Context context, AttributeSet attrs,   
         int defStyle) {   
       super(context, attrs, defStyle);   
       initViews(attrs);   
    }   
    public DottedPasswordLayout(Context context, AttributeSet attrs) {   
       super(context, attrs);   
       initViews(attrs);   
    }   
    @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) {   
       togglePasswordLabels(s.length());   
    }   
    public void togglePasswordLabels(int length) {   
       /*   
       * hide dot in every child box   
       */   
       for (int i = 0; i < dotLayout.getChildCount(); i++)   
         ((LinearLayout) dotLayout.getChildAt(i)).getChildAt(0)   
              .setVisibility(View.INVISIBLE);   
       /*   
       * Loop through each child and show dot if it falls under length   
       */   
       if (length <= dottedChildCount) {   
         while (length > 0) {   
            try {   
              ((LinearLayout) dotLayout.getChildAt(length - 1))   
                   .getChildAt(0).setVisibility(View.VISIBLE);   
              length--;   
            } catch (NullPointerException npe) {   
              Log.e("PasswordTextWatcher", "no such view");   
              return;   
            }   
         }   
       }   
    }   
    /**   
     * Gives you the password text   
     *    
     * @return   
     */   
    public Editable getText() {   
       if (null != edit_text)   
         return edit_text.getText();   
       return null;   
    }   
    public boolean setFocus() {   
       if (null != edit_text)   
         return edit_text.requestFocus();   
       return false;   
    }   
    @Override   
    public void onFocusChange(View v, boolean hasFocus) {   
       /*   
       * Toggle the Background of view on Focus changed.   
       */   
       if (dotLayout != null)   
         for (int i = 0; i < dotLayout.getChildCount(); i++)   
            dotLayout.getChildAt(i).setBackgroundResource(   
                 hasFocus ? R.drawable.square_focus : R.drawable.square);   
    }   
    /**   
     * Initiates views and adds dotted children based on max password chars   
     *    
     * @param attrs   
     */   
    private void initViews(AttributeSet attrs) {   
       LayoutInflater inflater = (LayoutInflater) getContext()   
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);   
       View v = inflater.inflate(R.layout.dotted_password_layout, this, true);   
       dotLayout = (LinearLayout) v.findViewById(R.id.dottedPwdLayout);   
       edit_text = (EditText) v.findViewById(R.id.edit_text);   
       edit_text.setOnFocusChangeListener(this);   
       edit_text.addTextChangedListener(this);   
       edit_text.setFocusableInTouchMode(true);   
       TypedArray a = getContext().obtainStyledAttributes(attrs,   
            R.styleable.DottedPasswordLayout);   
       dottedChildCount = a.getInteger(   
            R.styleable.DottedPasswordLayout_length, 4);   
       a.recycle();   
       for (int i = 0; i < dottedChildCount; i++) {   
         View child = inflater.inflate(R.layout.dotted_child, dotLayout,   
              false);   
         dotLayout.addView(child, dotLayout.getChildCount());   
       }   
    }   
  }   

In the above code snippet, editText is set with a TextWatcher and focus listener.

  • TextWacther listens to character changes in edit text and trigger the togglePasswordLabels() method which manages dots(child layouts inside Linear) visibility in the layout depending on length of text in edit text.
  • Focus listener manages background resource changes for all the dots, highlights them when this views gets into focus.

Create a custom attribute for the compound view to tell how many square layouts has to be added, this will limit length of hidden Edittext while initiating views in Compound view.

 <declare-styleable name="DottedPasswordLayout">   
    <attr name="length" format="integer" />   
   </declare-styleable>  

So, thats it. We are ready use this custom view in our activity layout.

 <?xml version="1.0" encoding="utf-8"?>   
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   
   xmlns:app="http://schemas.android.com/apk/res-auto"   
   android:layout_width="match_parent"   
   android:layout_height="match_parent"   
   android:orientation="vertical" >   
   <EditText   
    android:id="@+id/userName"   
    android:layout_width="match_parent"   
    android:layout_height="50dp"   
    android:layout_margin="10dp"   
    android:hint="User Name" />   
   <com.sample.sri.iphonestylepassword.DottedPasswordLayout   
    android:id="@+id/dottedPassword"   
    android:layout_width="match_parent"   
    android:layout_height="50dp"   
    android:layout_marginBottom="20dp"   
    android:layout_marginTop="20dp"   
    app:length="4" >   
   </com.sample.sri.iphonestylepassword.DottedPasswordLayout>   
   <Button   
    android:id="@+id/submit"   
    android:layout_width="match_parent"   
    android:layout_height="50dp"   
    android:layout_margin="10dp"   
    android:text="Submit" />   
  </LinearLayout>   

Here are some screenshots of demo:

                         device-2014-07-30-195317 device-2014-07-30-195357

NOTE: I haven’t concentrated much on dimensions, you can change as you need.
You can get access to source at https://github.com/Sriharia/CustomPasswordControl

Hope this helps 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s