In this tutorial, we’ll discuss and implement Android Percent Layout Support Library. Furthermore, we’ll be creating a Vertical TextView. Finally, we’ll merge the two concepts to code an interesting Login Screen design concept in our application.
Android Percent Layout Support Overview
Android Percent Layout support were introduced with the Android API 23. This library allows us to specify a percentage value for layout_width
, layout_height
and layout_margin
.
Therefore, a PercentRelativeLayout is a RelativeLayout with an added functionality of assigning weights to the child view(similarly PercentFrameLayout), a feature which has been always there in LinearLayouts.
Hence, we can put a percentage(out of 100) on child view components present in Percent Layout in terms of width, height, margins.
PercentRelativeLayout
and PercentFrameLayout
help us in reducing the view complexity since we’re no longer forced to encapsulate our child view with a LinearLayout and use weights for child views.
To use Percent Layout support, add the following dependency to the build.gradle file.
compile 'com.android.support:percent:25.3.1'
Adding the above dependency allows us to use android.support.percent.PercentRelativeLayout
and android.support.percent.PercentFrameLayout
in our application.
As a result, we can now replace the RelativeLayout and FrameLayout tags in our xml with PercentRelativeLayout and PercentFrameLayout respectively.
Android Percent Support Layout Example
Let’s see a sample implementation of layout_widthPercent
.
app:layout_widthPercent
: Here we set our views width in percentage.
Note: In PercentRelativeLayout, layout_width
and layout_height
attributes are optional if we have specified layout_widthPercent
or layout_heightPercent
attribute.
sample.xml Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?xml version="1.0" encoding="utf-8"?> <android.support.percent.PercentRelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.journaldev.percentlayouts.MainActivity"> <TextView android:layout_height="wrap_content" android:text="Hello World!" android:textSize="20sp" app:layout_widthPercent="75%" /> </android.support.percent.PercentRelativeLayout> |
Output :
We’ve set the layout_widthPercent
to 75 in the above implementation. In addition, setting it to 100 would be equivalent to match_parent. We can implement the other layout params that are given below in a similar manner.
layout_heightPercent
layout_marginPercent
layout_marginLeftPercent
layout_marginRightPercent
layout_marginTopPercent
layout_marginBottomPercent
layout_marginStartPercent
layout_marginEndPercent
Android Vertical TextView
The standard TextView widget that we use displays the text horizontally only. Hence, we’ll be creating a custom TextView that allows us to display the text vertically.
The code for the VerticalTextView.java
class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.journaldev.verticaltextviewandpercentlayout; import android.content.Context; import android.graphics.Canvas; import android.support.v7.widget.AppCompatTextView; import android.text.TextPaint; import android.util.AttributeSet; import android.view.Gravity; public class VerticalTextView extends AppCompatTextView { final boolean topDown; public VerticalTextView(Context context, AttributeSet attrs) { super(context, attrs); final int gravity = getGravity(); if (Gravity.isVertical(gravity) && (gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { setGravity((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP); topDown = false; } else topDown = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(heightMeasureSpec, widthMeasureSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); } @Override protected void onDraw(Canvas canvas) { TextPaint textPaint = getPaint(); textPaint.setColor(getCurrentTextColor()); textPaint.drawableState = getDrawableState(); canvas.save(); if (topDown) { canvas.translate(getWidth(), 0); canvas.rotate(90); } else { canvas.translate(0, getHeight()); canvas.rotate(-90); } canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop()); getLayout().draw(canvas); canvas.restore(); } } |
- By default, rotated text is from top to bottom. If we set
android:gravity="bottom"
, then it’s drawn from bottom to top. We save the state of the rotated text (top to bottom/ bottom to top) in the boolean variabletopDown
. - In the
onMeasure()
method we swap the width and height to draw the text rotated. - Finally, in the
onDraw()
method based on thetopDown
boolean flag, we apply a rotation dependent on the gravity setting.
Going further, let’s code an interesting Login UI Concept using Percent Layout support and Vertical TextView.
Android Percent Layout and Vertical TextView Project Structure
Take note of the xml files in the anim
folder. They’ll be used to animate between the sign in and register layouts and buttons.
Android Percent Layout and Vertical TextView Code
Add the following dependency in the build.gradle
file.
1 |
compile 'com.android.support:percent:25.3.1' |
The xml layout code for activity_main.xml
is given below :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" android:id="@+id/viewGroup" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentBottom="true" android:scaleType="centerCrop" android:src="https://www.journaldev.com/13937/@drawable/background" /> <android.support.percent.PercentRelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorAccentTrans" android:orientation="horizontal"> <LinearLayout android:id="@+id/llSign_in" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:orientation="horizontal" app:layout_widthPercent="85%"> <com.journaldev.verticaltextviewandpercentlayout.VerticalTextView android:id="@+id/txtSignIn" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:paddingTop="15dp" android:rotation="180" android:text="@string/sign_in" android:textAllCaps="true" android:textColor="#FFFFFF" android:textSize="26sp" android:visibility="gone" /> <include layout="@layout/layout_sign_in" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" /> </LinearLayout> <LinearLayout android:id="@+id/llRegister" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:background="@color/colorYTrans" android:orientation="horizontal" app:layout_widthPercent="15%"> <com.journaldev.verticaltextviewandpercentlayout.VerticalTextView android:id="@+id/txtRegister" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center" android:gravity="center" android:paddingTop="15dp" android:rotation="180" android:text="@string/register" android:textAllCaps="true" android:textColor="#FFFFFF" android:textSize="26sp" android:visibility="visible" /> <include layout="@layout/layout_register" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" /> </LinearLayout> </android.support.percent.PercentRelativeLayout> </RelativeLayout> |
Take note of the percentages specified in the above code.
We let the layout_sign_in.xml
occupy majority portion of the screen initially. Besides we’ve hidden the VerticalTextView
for SIGN IN. Eventually, these things would be reversed when the layout_register.xml
screen is laid.
The xml layout code for layout_sign_in.xml
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="24dp"> <EditText android:id="@+id/inEmail" android:layout_width="match_parent" android:layout_height="48dip" android:background="@drawable/input_field" android:hint="@string/email" android:imeOptions="actionNext" android:inputType="textEmailAddress" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inPassword" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_horizontal_margin" android:background="@drawable/input_field" android:hint="@string/password" android:imeOptions="actionDone" android:inputType="textPassword" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <Button android:id="@+id/btnSignIn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="@null" android:text="@string/sign_in" android:textColor="#FFF" android:textSize="28sp" android:textStyle="bold" /> <Button android:id="@+id/btnForgotPassword" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:background="@android:color/transparent" android:focusable="true" android:gravity="center" android:text="@string/forgot_password" android:textColor="#FFF" android:textSize="16sp" /> </LinearLayout> </RelativeLayout> |
The xml layout code for layout_register.xml
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:padding="24dp"> <EditText android:id="@+id/inFirstName" android:layout_width="match_parent" android:layout_height="48dip" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/first_name" android:imeOptions="actionNext" android:inputType="text" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inLastName" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_vertical_margin" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/last_name" android:imeOptions="actionNext" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inEmail" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_vertical_margin" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/email" android:imeOptions="actionNext" android:inputType="textEmailAddress" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inPassword" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_vertical_margin" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/password" android:imeOptions="actionNext" android:inputType="textPassword" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inConfirmPassword" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_vertical_margin" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:fontFamily="sans-serif-light" android:hint="@string/confirm_password" android:imeOptions="actionNext" android:inputType="textPassword" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <EditText android:id="@+id/inPhone" android:layout_width="match_parent" android:layout_height="48dip" android:layout_marginTop="@dimen/activity_vertical_margin" android:background="@drawable/input_field" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/phone" android:imeOptions="actionNext" android:inputType="phone" android:maxLines="1" android:paddingLeft="16.0dip" android:paddingRight="16.0dip" android:textColor="#FFF" android:textColorHint="#b3ffffff" android:textCursorDrawable="@null" /> <Button android:id="@+id/btnRegister" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="@null" android:text="@string/register" android:textColor="#FFF" android:textSize="28sp" android:textStyle="bold" /> </LinearLayout> </ScrollView> </RelativeLayout> |
In addition, the background for each EditText is defined under the drawable folder in the file shape.xml
as shown below.
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="https://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#1affffff"/> <corners android:radius="2.0dip"/> </shape> |
The code for the MainActivity.java
is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package com.journaldev.verticaltextviewandpercentlayout; import android.support.percent.PercentLayoutHelper; import android.support.percent.PercentRelativeLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity implements View.OnClickListener { VerticalTextView txtSignIn, txtRegister; LinearLayout llSignIn, llRegister; Button btnRegister, btnSignIn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); llSignIn = (LinearLayout) findViewById(R.id.llSign_in); llRegister = (LinearLayout) findViewById(R.id.llRegister); txtRegister = (VerticalTextView) findViewById(R.id.txtRegister); txtSignIn = (VerticalTextView) findViewById(R.id.txtSignIn); btnSignIn = (Button) findViewById(R.id.btnSignIn); btnRegister = (Button) findViewById(R.id.btnRegister); txtSignIn.setOnClickListener(this); txtRegister.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.txtSignIn: hideSoftKeyBoard(); showSignInForm(); break; case R.id.txtRegister: hideSoftKeyBoard(); showRegisterForm(); break; } } private void showSignInForm() { PercentRelativeLayout.LayoutParams paramsLogin = (PercentRelativeLayout.LayoutParams) llRegister.getLayoutParams(); PercentLayoutHelper.PercentLayoutInfo infoLogin = paramsLogin.getPercentLayoutInfo(); infoLogin.widthPercent = 0.15f; llRegister.requestLayout(); PercentRelativeLayout.LayoutParams paramsSignup = (PercentRelativeLayout.LayoutParams) llSignIn.getLayoutParams(); PercentLayoutHelper.PercentLayoutInfo infoSignup = paramsSignup.getPercentLayoutInfo(); infoSignup.widthPercent = 0.85f; llSignIn.requestLayout(); txtRegister.setVisibility(View.VISIBLE); txtSignIn.setVisibility(View.GONE); Animation translate = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.translate_left_to_right); llSignIn.startAnimation(translate); Animation clockwise = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rotate_left_to_right); btnSignIn.startAnimation(clockwise); } private void showRegisterForm() { PercentRelativeLayout.LayoutParams paramsLogin = (PercentRelativeLayout.LayoutParams) llSignIn.getLayoutParams(); PercentLayoutHelper.PercentLayoutInfo infoLogin = paramsLogin.getPercentLayoutInfo(); infoLogin.widthPercent = 0.15f; llSignIn.requestLayout(); PercentRelativeLayout.LayoutParams paramsSignup = (PercentRelativeLayout.LayoutParams) llRegister.getLayoutParams(); PercentLayoutHelper.PercentLayoutInfo infoSignup = paramsSignup.getPercentLayoutInfo(); infoSignup.widthPercent = 0.85f; llRegister.requestLayout(); txtRegister.setVisibility(View.GONE); txtSignIn.setVisibility(View.VISIBLE); Animation translate = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.translate_right_to_left); llRegister.startAnimation(translate); Animation clockwise = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rotate_right_to_left); btnRegister.startAnimation(clockwise); } private void hideSoftKeyBoard() { InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); if (imm.isAcceptingText()) { // verify if the soft keyboard is open imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); } } } |
Few inferences drawn from the above code are:
-
- We interchange the percentage widths(using float values) of the LinearLayouts held inside
PercentRelativeLayout
namelylayout_sign_in
andlayout_register
. - Besides we animate the above-mentioned layouts along with showing a transition from the
VerticalTextView
to its respective Button. hideSoftKeyboard()
is invoked every time aVerticalTextView
is clicked in order to dismiss any previously opened keyboard.
- We interchange the percentage widths(using float values) of the LinearLayouts held inside
Note: To hide the keyboard when the application launches, we set the windowSoftInputMode
to hidden
in the AndroidManifest.xml
file as shown below.
1 2 3 4 5 |
<activity android:name=".MainActivity" android:windowSoftInputMode="stateHidden"> |
Consequently, the output of the application in action is given below.
This brings an end to this tutorial. You can download the final Android VerticalTextViewAndPercentLayout Project from the link below.
Reference: android.support.percent