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
Toastto 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'