Android Percent Layout Support and Vertical TextView

Filed Under: Android

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


<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout 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: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 :

android percent layout support library example 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.

  1. layout_heightPercent
  2. layout_marginPercent
  3. layout_marginLeftPercent
  4. layout_marginRightPercent
  5. layout_marginTopPercent
  6. layout_marginBottomPercent
  7. layout_marginStartPercent
  8. 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:


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 variable topDown.
  • In the onMeasure() method we swap the width and height to draw the text rotated.
  • Finally, in the onDraw() method based on the topDown 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

android percent layout 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.

compile 'com.android.support:percent:25.3.1'

The xml layout code for activity_main.xml is given below :


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://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="@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.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://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.


<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout 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">


    <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.


<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://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.


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:

  1. We interchange the percentage widths(using float values) of the LinearLayouts held inside PercentRelativeLayout namely layout_sign_in and layout_register.
  2. Besides we animate the above-mentioned layouts along with showing a transition from the VerticalTextView to its respective Button.
  3. hideSoftKeyboard() is invoked every time a VerticalTextView is clicked in order to dismiss any previously opened keyboard.
  4. Note: To hide the keyboard when the application launches, we set the windowSoftInputMode to hidden in the AndroidManifest.xml file as shown below.

    
    <activity
                android:name=".MainActivity"
                android:windowSoftInputMode="stateHidden">
    

    Consequently, the output of the application in action is given below.
    android-percent-layout-vertical-textview-output

This brings an end to this tutorial. You can download the final Android VerticalTextViewAndPercentLayout Project from the link below.

Reference: android.support.percent

Comments

  1. Arunkumar says:

    side label are not showing sir ….

  2. khushbu says:

    AWESOME ANUPAM,
    UI looks so good by using this…. u r great man!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages