In this post, I will make a lock activity by designing in XML. There is an another solution is using a third-party library, please read my previous post.
Designing layout for lock activity
activity_lock.xml
Expected output:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
tools:context="info.devexchanges.lockscreen.LockActivity">
<LinearLayout
android:id="@+id/lock_keypad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn1"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="1" />
<Button
android:id="@+id/btn2"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="2" />
<Button
android:id="@+id/btn3"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="3" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn4"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="4" />
<Button
android:id="@+id/btn5"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="5" />
<Button
android:id="@+id/btn6"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="6" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn7"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="7" />
<Button
android:id="@+id/btn8"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="8" />
<Button
android:id="@+id/btn9"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="9" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_space"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:enabled="false" />
<Button
android:id="@+id/btn0"
style="@style/Borderless_Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0" />
<ImageView
android:id="@+id/btn_clear"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@null"
android:src="@android:drawable/ic_input_delete"
android:tint="@color/colorPrimary" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/dot_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/lock_keypad"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/dot_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/dot_disable" />
<ImageView
android:id="@+id/dot_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/dot_disable" />
<ImageView
android:id="@+id/dot_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/dot_disable" />
<ImageView
android:id="@+id/dot_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/dot_disable" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/dot_layout"
android:layout_centerInParent="true"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:src="@drawable/lock" />
</RelativeLayout>
Now, you must have a main activity, it's will start when launching app. It's layout depend on your work, in this simple project, it only include a
TextView
:
activity_main.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="info.devexchanges.lockscreen.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/activity_horizontal_margin"
android:text="Hello World!" />
</LinearLayout>
Main activity programmatically code
onStart()
of MainActivity
, we'll check if users haven't typed true before, starting LockActivity
immediately by Intent
! In order to detect users passed the lock screen or not, we'll use SharedPreferences
. Source code simple like this:
MainActivity.java
As you can see, we must remove password when the main activity closed (by override package info.devexchanges.lockscreen;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
}
@SuppressLint("SetTextI18n")
@Override
protected void onStart() {
super.onStart();
if (!isPass()) {
Intent intent = new Intent(this, LockActivity.class);
startActivity(intent);
} else {
textView.setText("The code you typed is right!");
}
}
private boolean isPass() {
SharedPreferences prefs = getSharedPreferences("PASS_CODE", MODE_PRIVATE);
return prefs.getBoolean("is_pass", false);
}
@Override
protected void onStop() {
super.onStop();
SharedPreferences.Editor editor = getSharedPreferences("PASS_CODE", MODE_PRIVATE).edit();
editor.putBoolean("is_pass", false);
editor.apply();
}
}
onStop()
to guaranteed that the lock activity always shown when app resumed!
Lock activity programmatically code
- Handle click event of all buttons in the keypad (to get input code - password)
- Update dots layout when input code changed
- Check input code right or wrong. If right, return to the main activity and if wrong, show a
Toast
to warning.
LockActivity.java
This is output when app running:
package info.devexchanges.lockscreen;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.List;
import butterknife.BindViews;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class LockActivity extends AppCompatActivity {
@BindViews({R.id.btn0, R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6,
R.id.btn7, R.id.btn8, R.id.btn9, R.id.btn_clear})
List<View> btnNumPads;
@BindViews({R.id.dot_1, R.id.dot_2, R.id.dot_3, R.id.dot_4})
List<ImageView> dots;
private static final String TRUE_CODE = "2869";
private static final int MAX_LENGHT = 4;
private String codeString = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lock);
ButterKnife.bind(this);
}
@OnClick(R.id.btn_clear)
public void onClear() {
if (codeString.length() > 0) {
//remove last character of code
codeString = removeLastChar(codeString);
//update dots layout
setDotImagesState();
}
}
@OnClick({R.id.btn0, R.id.btn1, R.id.btn2, R.id.btn3, R.id.btn4, R.id.btn5, R.id.btn6,
R.id.btn7, R.id.btn8, R.id.btn9})
public void onClick(Button button) {
getStringCode(button.getId());
if (codeString.length() == MAX_LENGHT) {
if (codeString.equals(TRUE_CODE)) {
Toast.makeText(this, "Code is right", Toast.LENGTH_SHORT).show();
setIsPass();
finish();
} else {
Toast.makeText(this, "Wrong Pass code", Toast.LENGTH_SHORT).show();
//vibrate the dots layout
shakeAnimation();
}
} else if (codeString.length() > MAX_LENGHT){
//reset the input code
codeString = "";
getStringCode(button.getId());
}
setDotImagesState();
}
private void shakeAnimation() {
Animation shake = AnimationUtils.loadAnimation(this, R.anim.vibrate_anim);
findViewById(R.id.dot_layout).startAnimation(shake);
Toast.makeText(this, "Wrong Password", Toast.LENGTH_SHORT).show();
}
private void getStringCode(int buttonId) {
switch (buttonId) {
case R.id.btn0:
codeString += "0";
break;
case R.id.btn1:
codeString += "1";
break;
case R.id.btn2:
codeString += "2";
break;
case R.id.btn3:
codeString += "3";
break;
case R.id.btn4:
codeString += "4";
break;
case R.id.btn5:
codeString += "5";
break;
case R.id.btn6:
codeString += "6";
break;
case R.id.btn7:
codeString += "7";
break;
case R.id.btn8:
codeString += "8";
break;
case R.id.btn9:
codeString += "9";
break;
default:
break;
}
}
private void setDotImagesState() {
for (int i = 0; i < codeString.length(); i++) {
dots.get(i).setImageResource(R.drawable.dot_enable);
}
if (codeString.length()<4) {
for (int j = codeString.length(); j<4; j++) {
dots.get(j).setImageResource(R.drawable.dot_disable);
}
}
}
private String removeLastChar(String s) {
if (s == null || s.length() == 0) {
return s;
}
return s.substring(0, s.length() - 1);
}
private void setIsPass() {
SharedPreferences.Editor editor = getSharedPreferences("PASS_CODE", MODE_PRIVATE).edit();
editor.putBoolean("is_pass", true);
editor.apply();
}
}
Shaking animation
res\anim\shake_anim.xml
And this is output:<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="70"
android:fromDegrees="-5"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="@android:anim/linear_interpolator"
android:toDegrees="5" />
<translate
android:fromXDelta="-10"
android:toXDelta="10"
android:repeatCount="5"
android:repeatMode="reverse"
android:interpolator="@android:anim/linear_interpolator"
android:duration="70" />
</set>
You can make other better animation than this by research more about animation in Android!
In this project, I use ButterKnife to make
finViewbyId
work become easier! In order to use this library, please add this dependency to your app-level build.gradle:
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'