کار با TextInputLayout و Floating Label متریال دیزاین
معرفی TextInputLayout:
بهنام خدا. در این مبحث به قابلیتی با عنوان Floating Label میپردازیم که بر روی EditText اجرا میشود. در گذشته با Hint آشنا شدیم. با استفاده از این ویژگی میتوانستیم یک توضیح کوتاه به یک EditText اضافه کنیم که با پر شدن فیلد توسط کاربر، Hint نیز مخفی میشد. اما برای زیبایی بیشتر فیلدهایی که دارای Hint میباشند میتوانیم قابلیتی پیاده سازی کنیم که با لمس EditText توسط کاربر و وارد کردن مقدار، Hint حذف نشده و با یک حالت انیمیشن به بالای فیلد منتقل شود. برای پیاده سازی این قابلیت به کتابخانه Support Design و تگ TextInputLayout نیاز داریم. ضمنا از این کامپوننت برای نمایش Error ها نیز استفاده میشود.
یک پروژه جدید با نام TextInputLayout و یک Empty Activity ایجاد میکنم.
در ابتدا لازم است کتابخانه design را به پروژه اضافه کنم:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:design:27.1.1' }
قصد دارم دو EditText برای دریافت نام کاربری و رمز عبور کاربر به اکتیویتی اضافه کنم:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context=".MainActivity"> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_layout"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_edittext" android:hint="Username"/> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword"/> </android.support.design.widget.TextInputLayout> </LinearLayout>
هر EditText درون یک تگ TextInputLayout قرار میگیرد.
پروژه را اجرا میکنم:
با اجرای پروژه، اولین EditText یعنی Username در حالت Focus قرار میگیرد که آماده وارد کردن کاراکتر است. هر آیتمی که در حالت Focus باشد رنگ Hint و خط زیر آن قرمز و مابقی آیتمها خاکستری است.
ممکن است بخواهیم آیتمی غیر از آیتم اول در حالت Focus باشد. یا بهرحال تعیین کنیم یک آیتم خاص به عنوان آیتم Focus شده نمایش داده شود. به کد زیر دقت کنید:
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword"/> <requestFocus/> </android.support.design.widget.TextInputLayout>
من یک تگ requestFocus به TextInputLayout مربوط به آیتم Password اضافه کردم که در اجرای مجدد پروژه هم این فیلد Focus شده:
در حال حاضر همیشه یک EditText باید در حالت Focus باشد. اما بهتر است حالتی را پیاده سازی کنیم که اگر کاربر نقطه ای از صفحه (بجز محل این دو آیتم) را لمس کند، آیتمی که Focus هست از این حالت خارج شده و در نهایت تمامی آیتم ها در حالت عادی نمایش داده شوند.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" tools:context=".MainActivity" android:focusable="true" android:focusableInTouchMode="true" android:id="@+id/main_layout"> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_layout"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_edittext" android:hint="Username"/> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword"/> <requestFocus/> </android.support.design.widget.TextInputLayout> </LinearLayout>
Layout ریشه اکتیویتی یک LinearLayout است. به این لایه دو خاصیت focusable و focusableInTouchMode با مقدار true و یک id اضافه کردم. سپس در Backend اکتیویتی این لایه را تعریف نموده و برای آن یک متد Listener میسازم که ورودی آن null است:
MainActivity.java:
package ir.android_studio.textinputlayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity { LinearLayout mLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.main_layout); mLayout.setOnClickListener(null); } }
مقدار null برای این setOnClickListener باعث میشود تا هنگام کلیک (لمس) صفحه که من از نوع LinearLayout ساخته ام، همه EditText ها در حالت غیر Focus قرار گیرند:
ضمنا با اضافه شدن دو خاصیت فوق به لایه اصلی، هیچکدام از آیتم ها در ابتدای اجرای پروژه به صورت پیش فرض Focus نیستند.
قبل از ادامه آموزش، تگ requestFocus را هم غیر فعال (کامنت) میکنم زیرا نمیخواهم هیچ آیتمی فوکوس باشد.
مدیریت Error ها در TextInputLayout:
مطمئنا همه ما هنگام تکمیل فرم ها به ارورهایی برخورد کرده ایم. این فرم میتواند یک فرم ثبت نام در یک وب سایت یا یک اپلیکیشن باشد. به عنوان مثال در فیلد مربوط به کد پستی، اخطار “کد وارد شده باید ده رقم باشد” به کاربر اعلام میشود.
تصمیم دارم برای فیلد Username یک error تعریف کنم.
package ir.android_studio.textinputlayout; import android.support.design.widget.TextInputLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity { LinearLayout mLayout; TextInputLayout userLayout, passLayout; EditText userEdittext, passEdittext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.main_layout); mLayout.setOnClickListener(null); userLayout = findViewById(R.id.user_layout); passLayout = findViewById(R.id.pass_layout); userEdittext = findViewById(R.id.user_edittext); passEdittext = findViewById(R.id.pass_edittext); userEdittext.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean b) { if (userEdittext.getText().toString().isEmpty()) { userLayout.setError("Enter Username!"); } else { userLayout.setErrorEnabled(false); } } }); } }
در کد بالا از متد setOnFocusChangeListener استفاده شده:
userEdittext.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean b) { } });
این متد هنگامی اجرا میشود که کاربر روی یک EditText ضربه بزند و آن فیلد در حالت فوکوس قرار بگیرد. متد را به اینصورت تکمیل میکنم:
userEdittext.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean b) { if (userEdittext.getText().toString().isEmpty()) { userLayout.setError("Enter Username!"); } else { userLayout.setErrorEnabled(false); } } });
یک شرط تعیین کردم که اگر EditText خالی بود، توسط متد setError یک ارور را روی userLayout نمایش بدهد و زمانی که این شرط نقض شد (یعنی فیلد خالی نباشد) ارور غیر فعال شود.
پروژه را اجرا میکنم:
ملاحظه میکنید با لمس EditText مربوط به Username بلافاصله اخطار با رنگ قرمز زیر آن نمایش داده میشود. در حال حاضر برنامه یک ایراد دارد. با وجود اینکه من کلمه test را تایپ کردهام هنوز اخطار Enter Username حذف نشده و زمانی حذف خواهد شد که کاربر EditText بعدی یا جای دیگری از صفحه را لمس کند تا این آیتم از Focus خارج شود. در صورتی که به محض ورود اولین کاراکتر این ارور باید حذف شود.
با استفاده از متد addTextChangedListener و TextWatcher میتوانیم تغییرات متن یک EditText را شنود کنیم.
userEdittext.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void afterTextChanged(Editable editable) { } });
TextWatcher سه متد دارد که نقش هرکدام از نامش پیداست. beforeTextChanged قبل از تغییر متن، onTextChanged هنگام تغییر متن و afterTextChanged بعد از تغییر متن صدا زده میشوند.
در اینجا من به متد دوم نیاز دارم. همان if ای که قبلا نوشتم را درون این متد تکرار میکنم:
package ir.android_studio.textinputlayout; import android.support.design.widget.TextInputLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.EditText; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity { LinearLayout mLayout; TextInputLayout userLayout, passLayout; EditText userEdittext, passEdittext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.main_layout); mLayout.setOnClickListener(null); userLayout = findViewById(R.id.user_layout); passLayout = findViewById(R.id.pass_layout); userEdittext = findViewById(R.id.user_edittext); passEdittext = findViewById(R.id.pass_edittext); userEdittext.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean b) { if (userEdittext.getText().toString().isEmpty()) { userLayout.setError("Enter Username!"); } else { userLayout.setErrorEnabled(false); } } }); userEdittext.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { if (userEdittext.getText().toString().isEmpty()) { userLayout.setError("Enter Username!"); } else { userLayout.setErrorEnabled(false); } } @Override public void afterTextChanged(Editable editable) { } }); } }
حالا با وارد کردن اولین کاراکتر، اخطار حذف میشود:
با استفاده از خاصیت textColorHint میتوان رنگ Hint یا همان Floating Label را تغییر داد:
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_layout" android:textColorHint="#36cb1f"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_edittext" android:hint="Username"/> </android.support.design.widget.TextInputLayout>
خاصیت hintTextAppearance هم برای استایل دهی به Hint هنگامی که Focus شده، بکار میرود:
<style name="TextInputStyle" parent="TextAppearance.AppCompat"> <item name="android:textColor">#434ab2</item> <item name="android:textSize">16dp</item> </style>
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_layout" android:textColorHint="#36cb1f" app:hintTextAppearance="@style/TextInputStyle"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_edittext" android:hint="Username"/> </android.support.design.widget.TextInputLayout>
بهتر است استایلی که تعریف میکنیم از TextAppearance.AppCompat ارث بری کند.
متن Error را هم به همین صورت و توسط خاصیت errorTextAppearance میتوان استایل دهی کرد:
<style name="ErrorInputStyle" parent="TextAppearance.AppCompat"> <item name="android:textColor">#731539</item> <item name="android:textSize">16dp</item> </style>
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_layout" android:textColorHint="#36cb1f" app:hintTextAppearance="@style/TextInputStyle" app:errorTextAppearance="@style/ErrorInputStyle"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/user_edittext" android:hint="Username"/> </android.support.design.widget.TextInputLayout>
در TextInputLayout قابلیت دیگری با نام Counter (شمارنده) تعریف شده. کاربرد شمارنده در مواقعی است که بخواهیم تعداد کاراکترهای مناسب را به کاربر اعلام کنیم:
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout" app:counterEnabled="true" app:counterMaxLength="8"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword" /> <!-- <requestFocus/> --> </android.support.design.widget.TextInputLayout>
خاصیت counterEnabled برای فعال کردن این ویژگی و counterMaxLength برای تعیین تعداد کاراکتر استفاده شده است:
با وارد کردن هر کاراکتر درون این EditText یک شماره به عدد سمت چپ اضافه میشود.
در صورتی که تعداد کاراکتر ورودی از مقدار MaxLength بیشتر شود، رنگ شمارنده تغییر میکند. تعیین رنگ شمارنده در حالت عادی و حالتی که تعداد کاراکترها از مقدار تعیین شده تجاوز کند به ترتیب توسط دو خاصیت counterTextAppearance و counterOverflowTextAppearance صورت میپذیرد:
<style name="CounterStyle" parent="TextAppearance.AppCompat"> <item name="android:textColor">#30c7ae</item> </style> <style name="OverflowStyle" parent="TextAppearance.AppCompat"> <item name="android:textColor">#199b85</item> </style>
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout" app:counterEnabled="true" app:counterMaxLength="8" app:counterTextAppearance="@style/CounterStyle" app:counterOverflowTextAppearance="@style/OverflowStyle"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword" /> <!-- <requestFocus/> --> </android.support.design.widget.TextInputLayout>
خاصیت passwordToggleEnabled آیکونی به EditText های از نوع password اضافه میکند که کاربر با لمس آن، پسوردی که وارد کرده را میتواند مشاهده میکند:
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_layout" app:counterEnabled="true" app:counterMaxLength="8" app:counterTextAppearance="@style/CounterStyle" app:counterOverflowTextAppearance="@style/OverflowStyle" app:passwordToggleEnabled="true"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/pass_edittext" android:hint="Password" android:inputType="textPassword" /> <!-- <requestFocus/> --> </android.support.design.widget.TextInputLayout>
توجه : سورس پروژه درون پوشه Exercises قرار داده شده است
منابع تکمیلی:
https://developer.android.com/reference/android/support/design/widget/TextInputLayout
https://developer.android.com/reference/com/google/android/material/textfield/TextInputLayout
https://developer.android.com/reference/android/text/TextWatcher
تعداد صفحات : ۲۱
حجم : ۱/۳ مگابایت
قیمت : رایگان
دانلود رایگان با حجم ۱/۳ مگابایت لینک کمکی
سلام.وقت بخیر.من ویژگی passwordToggleEnabled را که اضافه میکنم آیکون چشم برعکس کار میکند.یعنی وقتی که پسورد نمایش داده میشه خط روش هست و برعکس.از ‘com.google.android.material:material:1.3.0’ هم استفاده میکنم.ربطی داره؟خیلللی گشتم ولی ایرادو پیدا نمیکنم
وضعیت فعلی دکمه، عملی که با کلیک کردن انجام میده رو نشون میده. یعنی وقتی پسورد نمایش داده میشه باید روی آیکون خط مورب کشیده باشه که یعنی با کلیک روی دکمه پسورد مخفی میشه. بنابراین مشکلی وجود نداره
سلام
ایا به جای استفاده از linearlayout از چیدمان دستی خود اندروید استدیو استفاده کنیم و ویو ها رو یکی یکی به هم ربط بدیم تاثیری توی سرعت اپ داره یا اصلا منطقی هست
اگه منظورتون ConstraintLayout هست که به لحاظ سرعت بعید میدونم تفاوتی داشته باشه ولی خب یه لایه بندی جدید هست و کار رو ساده کرده و اگه بتونید باهاش کار کنید گزینه مناسبی هست. به امید خدا آموزش استفاده از این layout هم تهیه خواهد شد
سلام، خسته نباشید
من مشکلم اینه که کتابخانه design رو که وارد میکنم، کلا ارور میده، minSDK 17 هستش، ولی به ۲۹ هم تغییرش دادم و باز همون خطا رو میده
خطای Resolve میده!
مشکل از کجاست؟
پرسش های رایج رو مطالعه کنید بزرگوار. قسمت تغییر IP
آموزش ها تون خیلی خیلی عالی هستن مرسی.
اگه امکانش هست یک آموزش هم برای ماسک کردن EditText درست کنید.
سلام
جداً آموزش بسیار بسیار عالی بود .
خسته نباشی
سلام، خسته نباشید. واقعا ممنون به خاطر آموزش های خوب و کاملتون و همچنین قیمت خیلی مناسبشون
تشکر
سلام
چگونه میشه Outlined text field ساخت؟
سلام همشهری خوب هستید؟
چندین بار توی نتایج سایتتون رو دیده بودم فکر نمی کردم مدیر سایت همشهری خودمون باشه …
خلاصه کار ته خیله درستس همشری 🙂
ممنون بزرگوار. موفق باشید
سلام
اموزشتون خیلی عالی بود
تشکر میکنم
واقعا خسته نباشین