آموزش ساخت صفحه ثبت نام و ورود در اپ اندرویدی

آموزش ساخت صفحه ثبت نام و ورود در اپ اندرویدی

بروزرسانی : 11 ماه پیش

سلام امروز میخام بهتون یاد بدم چطور یک صفحه ی زیبای ثبت نام و ورود در اپلیکیشن های اندرویدی خودتون بسازید. بسیاری از اپلیکیشن ها سیستم احراز هویت خودشان را با استفاده از جامعه های مجازی مثل فیسبوک و توئیتر و گوگل انجام میدهند یا اینکه ایمیل و پسورد از کاربر دریافت میکنند تا احراز هویت انجام دهند. هدف ما اینجا برنامه نویسی یک سیستم احراز هویت ساده و راحت برای کاربران اپلیکیشن های شماست. حتما دوست دارید قسمت ثبت نام و ورود اپلیکیشن شما هیجان انگیز و زیبا باشه خب پس این آموزش رو ادامه دهید و مرحله به مرحله کدهای زیر را بنویسید تا نتیجه را ببینید . 

 

ساخت ثبت نام ورود در اپ اندرویدی رو از کجا شروع کنیم ؟‌

من برای یکی از اپلیکیشن های خودم پیگیر ساخت یک صفحه ورود / ثبت نام بودم که به دیزاین زیر از سایت uplabs.com متوسل شدم : 

آموزش ساخت و طراحی صفحه ورود و ثبت نام در اپلیکیشن اندروید

من تصمیم گرفتم ایده ی بالا را با کد در محیط اندروید استودیو پیاده سازی کنم و همچنین آموزش ساخت صفحه ی ورود و ثبت نام مشابه تصویر بالا را از صفر ، در این مقاله در اختیار شما قرار دهم . 

ساختار برنامه ی ورود و ثبت نام اندرویدی 

 بغیر از ایجاد یک رابط گرافیکی جذاب و زیبا ، قصد داریم کدهایی را در اختیار شما قرار دهیم که واقعا قابل استفاده باشد و نگرانی های اصلی شما برای ساختن چنین بخش هایی از اپلیکیشن را برطرف کنیم . بطوری که صفحه ی ثبت نام و ورود باید از هم دیگر جدا باشند . 

بخش هایی که برای ساختن این پروژه نیاز است  : 

  • تصویر بک گراند 
  • صفحه ی ثبت نام
  • صفحه ی ورود
  • منوی کناری یا فضای کناری که در صفحه دیده میشود
  • فیلد های ورودی
  • لوگو ، دکمه ی های زیرین صفحه و برچسب های صفحه ی ثبت نام و ورود 

یکبار دیگر تصویر زیر را نگاه کنید تا بخش هایی که بالا معرفی کردیم را کامل ببینید و درک کنید : 

آموزش طراحی صفحه ی ثبت نام و ورود در اندروید

برای پیاده سازی این View  ، هر صفحه میتواند یک Fragment باشد و میتوانیم از یک ViewPager استفاده کنیم برای اینکه بتوانیم بین صفحات سوئیچ کنیم . بک گراند ، لوگو و دکمه ها میتواند یک ImageView  ساده باشد .

پس اپلیکیشن باید ساختار زیر را داشته باشد : 

  • Activity به همراه یک ViewPager
  • Adapter
  • Login Fragment
  • Sign Up Fragment

در این آموزش با زبان برنامه نویسی java کار میکنیم که البته زبان جدید برنامه نویسی اندروید با کاتلین است ولی مشکلی نیست شما میتوانید با مبدل درون اندروید استودیو کدهای جاوا را به راحتی به کاتلین تبدیل کنید و سپس استفاده کنید.

نوار لبه را چطور پیاده سازی کنیم ؟ 

خب یکی از دلایل انتخاب ViewPager دارا بودن همین قسمت بود . شما با استفاده از متد getPageWidth میتوانید این قسمت را ایجاد کنید . با استفاده از این متد میتوان در سمت راست یا چپ صفحه یک نواری ایجاد کرد که اندازه ی آن نیز دلخواه است 

تصویر زیر را ببینید :  ( ایجاد لبه در کنار صفحات سمت راست و چپ با استفاده از getPageWidth )

آموزش طراحی و ساخت صفحه ی ورود یا ثبت نام در اپ اندروید

شاید با دیدن تصویر بالا این سوال به ذهن شما بیاید که چطور سایز استاندارد گپ یا نوار کناری را درست تنظیم میکنیم ؟ یا اینکه چطور بتوانیم یک متن را در نوار کناری جای دهیم که نمایش داده شود ؟ 

نگران نباشید ! ما یک متغیر factor ایجاد خواهیم کرد که در متد سازنده ی Adapter محاسبه خواهد شد و در متد getPageWidth استفاده خواهد شد . 

متغیر factor در کدهای زیر را ببینید : 

public static class Adapter extends FragmentStatePagerAdapter{
  //our factor value
  private float factor;
  
  public Adapter(FragmentManager manager, final ViewPager pager){
    super(manager);
    final float textSize = pager.getResources().getDimension(R.dimen.folded_size);
    final float textPadding = pager.getResources().getDimension(R.dimen.folded_label_padding);
    factor = 1 - (textSize + textPadding) / (pager.getWidth());
  }
  
  @Override
  public float getPageWidth(int position) {
    return factor;
  }
}

تحلیل کد بالا : 

  1. textSize  اندازه ی متنی هست که در نوار کناری قرار خواهد گرفت .
  2. textPadding میزان حاشیه از سمت چپ و راست میباشد . 
  3. ما میزان textSize و textPadding را با استفاده از تقسیم عرض صفحه نمایش محاسبه کرده ایم 

 

Activity مربوط به احراز هویت 

زمان این رسیده اصلی ترین قسمت طراحی خود یعنی اکتیویتی مربوط به احراز هویت را بسازیم. یک فایل Xml میسازیم به نام activity_login.xml و محتویات آنرا به شکل زیر کد نویسی میکنیم : 

 

activity_login.xml

    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/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vpaliy.loginconcept.LoginActivity">

            android:id="@+id/scrolling_background"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        tools:src="@drawable/busy"
        android:scaleX="@dimen/start_scale"
        android:scaleY="@dimen/start_scale"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

            android:id="@+id/pager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@color/color_log_in"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0" />

            android:src="@drawable/facebook"
        android:layout_width="@dimen/option_size"
        android:layout_height="@dimen/option_size"
        android:id="@+id/first"
        app:layout_constraintRight_toLeftOf="@+id/second"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.95" />

            android:src="@drawable/linkedin"
        android:layout_width="@dimen/option_size"
        android:layout_height="@dimen/option_size"
        android:id="@+id/second"
        app:layout_constraintLeft_toRightOf="@+id/first"
        app:layout_constraintRight_toLeftOf="@+id/last"
        app:layout_constraintTop_toTopOf="@+id/first"
        app:layout_constraintBottom_toBottomOf="@+id/first"
        app:layout_constraintVertical_bias="0.0" />

            android:src="@drawable/twitter"
        android:id="@+id/last"
        android:layout_width="@dimen/option_size"
        android:layout_height="@dimen/option_size"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/second"
        app:layout_constraintBottom_toBottomOf="@+id/second"
        app:layout_constraintTop_toTopOf="@+id/second"
        app:layout_constraintVertical_bias="1.0" />

            android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/guideline"
        app:layout_constraintGuide_begin="@dimen/guideline_margin"
        android:orientation="horizontal" />

            android:id="@+id/logo"
        android:focusable="true"
        android:src="@drawable/log"
        android:focusableInTouchMode="true"
        app:layout_constraintTop_toTopOf="@+id/guideline"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_width="@dimen/logo_size"
        android:layout_height="@dimen/logo_size"/>

 

نتیجه ی کدهای بالا باید مشابه تصویر زیر باشد : 

آموزش طراحی صفحه ی ورود و ثبت نام در اپلیکیشن اندروید

از ConstraintLayout بعنوان view اصلی این صفحه استفاده کردیم با ConstraintLayout میتوانید view های پیچیده و ریسپانسیو را به راحتی بسازید .

در این آموزش ما فرض میکنیم شما با ConstraintLayout آشنا هستید و اگر با آن آشنا نیستید به راحتی میتوانید با سرچ کردن در مورد آن مطالعه کنید یا اینکه سایت اصلی توسعه دهندگان اندروید رفته و از اینجا مقاله ی مربوط به ConstraintLayout را بخوانید . 

 

 AnimatedViewPager چیست ؟ 

شاید AnimatedViewPager در کدهای بالا برای شما نا آشنا باشد . AnimatedViewPager یک ویو اختصاصی شده است که هیچ عکس العملی نسبت به تاچ کردن و تغییر در حین کشیدن ( swipe )، نمیدهد.

لوگو و دکمه ها 

این المان ها باید در بین صفحات ورود و ثبت نام به اشتراک گذاشته شود . پس کار معقولانه ای میباشد که آنها را در یک جای مشخصی نگهداری کنیم لوگو و دکمه ها imageview های ساده ای هستند.

خب وقت آن رسیده کمی کدنویسی جاوا انجام دهیم.

اکتیویتی احراز هویت را به شکل زیر ایجاد و کدنویسی کنید 

 

AuthActivity.java 

public class AuthActivity extends AppCompatActivity {

  @BindViews(value = {R.id.logo, R.id.first, R.id.second, R.id.last})
  protected List sharedElements;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    ButterKnife.bind(this);
    final AnimatedViewPager pager = ButterKnife.findById(this, R.id.pager);
    final ImageView background = ButterKnife.findById(this, R.id.scrolling_background);
    int[] screenSize = screenSize();

    sharedElements.forEach(element -> {
      @ColorRes int color = element.getId() != R.id.logo ? R.color.white_transparent : R.color.color_logo_log_in;
      DrawableCompat.setTint(element.getDrawable(), ContextCompat.getColor(this, color));
    });
    //load a very big image and resize it, so it fits our needs
    Glide.with(this)
            .load(R.drawable.busy)
            .asBitmap()
            .override(screenSize[0] * 2, screenSize[1])
            .diskCacheStrategy(DiskCacheStrategy.RESULT)
            .into(new ImageViewTarget(background) {
              @Override
              protected void setResource(Bitmap resource) {
                background.setImageBitmap(resource);
                background.scrollTo(-pager.getWidth(), 0);
                background.post(() -> {
                  //we need to scroll to the very left edge of the image
                  //fire the scale animation
                  ObjectAnimator xAnimator = ObjectAnimator.ofFloat(background, View.SCALE_X, 4f, background.getScaleX());
                  ObjectAnimator yAnimator = ObjectAnimator.ofFloat(background, View.SCALE_Y, 4f, background.getScaleY());
                  AnimatorSet set = new AnimatorSet();
                  set.playTogether(xAnimator, yAnimator);
                  set.setDuration(getResources().getInteger(R.integer.duration));
                  set.start();
                });
                AuthAdapter adapter = new AuthAdapter(getSupportFragmentManager(), pager, background, sharedElements);
                pager.setAdapter(adapter);
              }
            });
  }

  private int[] screenSize() {
    Display display = getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    return new int[]{size.x, size.y};
  }
}

در کد بالا تصویر بک گراند لود شده و تنظیم میشود و همچنین یک Adapter برای ViewPager ساخته میشود  .

 

رنگ دهی به المان هایی که به اشتراک گذاشته شده است 

همانطور که قبلا گفته شد بخش هایی از ویو ها به اشتراک گذاشته میشود . المان های زیرین باید یک transparency داشته باشند برای مثال رنگ #B3FFFCFC را به آنها اختصاص میدهیم (رنگ سفید ساده با opacity 173 ) و برعکس المان های زیرین ، لوگو زمانی که به صفحه ی دیگر میرویم باید تغییر پیدا کند ولی برعکس  ، المان های زیرین ثابت هستند.

با استفاده از کد زیر رنگ دهی را انجام میدهیم : 

 

Shared.java

sharedElements.forEach(element -> {
  @ColorRes int color = element.getId() != R.id.logo ? R.color.white_transparent : R.color.color_logo_log_in;
  DrawableCompat.setTint(element.getDrawable(), ContextCompat.getColor(this, color));
});

 

توجه کنید که رنگ لوگو رو در adapter انجام میدیم . 

 

تصویر پس زمینه 

ما باید تصویری انتخاب کنیم که در پس زمینه کاملا کشیده باشه و پس زمینه را پر بکنه برای کیفیت بهتر تصویری با مشخصات size 1,900 x 1,200 JPEG, 24-bit color انتخاب کنید . 

بعد از انتخاب تصویر به راحتی عرض صفحه ی نمایش را دریافت کرده و تصویر را با استفاده از Glide, Picasso, Fresco یا هر کتابخانه ی دیگری که استفاده میکنید لود کنید کد زیر را ببینید : 

 

GlideLoading.java

//load a very big image and resize it, so it fits our needs
Glide.with(this)
 .load(R.drawable.busy)
 .asBitmap()
 .override(screenSize[0]*2,screenSize[1])
 .diskCacheStrategy(DiskCacheStrategy.RESULT)
 .into(new ImageViewTarget(background) {
   @Override
   protected void setResource(Bitmap resource) {
      background.setImageBitmap(resource);
      background.post(()->{
        //we need to scroll to the very left edge of the image
        background.scrollTo(-background.getWidth()/2,0);
        //fire the scale animation
        ObjectAnimator xAnimator=ObjectAnimator.ofFloat(background,View.SCALE_X,4f,background.getScaleX());
        ObjectAnimator yAnimator=ObjectAnimator.ofFloat(background,View.SCALE_Y,4f,background.getScaleY());
        AnimatorSet set=new AnimatorSet();
        set.playTogether(xAnimator,yAnimator);
        set.setDuration(getResources().getInteger(R.integer.duration));
        set.start();
      });
      AuthAdapter adapter = new AuthAdapter(getSupportFragmentManager(), pager, background, sharedElements);
      pager.setAdapter(adapter);
    }
});

 

  1. بعد از اینکه تصویر لود شد شما بایستی با استفاده از متد ImageView.scrollTo تمرکز کاربر را به سمت چپ جذب کنید 
  2. بعد از آن ، نیاز داریم یک انیمیشن بزرگنمایی به آن اضافه کنیم 
  3. و سپس adapter را مقدار دهی اولیه بکنیم 

Adapter  و محتویات آن

ما باید یک کنترل کننده داشته باشیم تا متوجه شویم بین fragment ها چه اتفاقی می افتد که بهترین گزینه برای این کار adapter ما میباشد . 

ما باید یک کلاس پایه برای fragment ها داشته باشیم که بعنوان رابط توسط Adapter  باشد مثل کد زیر  : 

AuthFragment1.java

public abstract class AuthFragment extends Fragment {

  protected Callback callback;

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View root = inflater.inflate(authLayout(), container, false);
    ButterKnife.bind(this, root);
    return root;
  }

  public void setCallback(@NonNull Callback callback) {
    this.callback = callback;
  }

  @OnClick(R.id.caption)
  public void unfold() {
        /* animation goes here 
           ...   ....
        */
    //after everything's been set up, tell the ViewPager to flip the page
    callback.show(this);
  }

  @LayoutRes
  public abstract int authLayout();

  public abstract void fold();

  public abstract void clearFocus();

  interface Callback {
    void show(AuthFragment fragment);

    void scale(boolean hasFocus);
  }
}

همانطور که در کد بالا میبینید ما برای هر انیمیشن سه متد داریم . به این ترتیب ، شما میتوانید به هر دو fragment اشاره کنید.

برای  جلوگیری از هر گونه سردرگمی بیائید کدهای بالا را تحلیل کنیم  :

  • fold زمانی صدا زده میشود که شما بین صفحه ی بعدی یا قبلی سوئیچ میکنید 
  • unfold قبل از سوئیچ کردن بین صفحات صدا زده میشود . این متد توسط adapter صدا زده نمیشود بلکه زمانی صدا زده میشود که یک رویداد کلیک اتفاق بیوفتد مثلا زمانی که شما روی TextView عمودی که در کنار هست کلیک کنید 
  • clearFocus زمانی رخ میدهد که یک input ترک شود مثلا پسورد و ایمیل وارد شده باشد 
  • authLayout یک منبعی هست برای متد Fragment.onCreateView 

 AuthFragment.Callback

ما باید یک راهی پیدا کنیم که تمامی تغییرات fragment ها را به adapter اطلاع رسانی کنیم که این کار را با متد  AuthFragment.Callback انجام میدهیم 

کد کامل adapter ما به شکل زیر است 

CompleteAuthAdapter.java

 

public class AuthAdapter extends FragmentStatePagerAdapter
        implements AuthFragment.Callback {
        
  private final AnimatedViewPager pager;
  private final SparseArray authArray;
  private final List sharedElements;
  private final ImageView authBackground;
  private float factor;
  
  public AuthAdapter(FragmentManager manager, AnimatedViewPager pager,
                     ImageView authBackground, List sharedElements) {
    super(manager);
    this.authBackground = authBackground;
    this.pager = pager;
    this.authArray = new SparseArray<>(getCount());
    this.sharedElements = sharedElements;
    pager.setDuration(pager.getResources().getInteger(R.integer.duration));
    final float textSize = pager.getResources().getDimension(R.dimen.folded_size);
    final float textPadding = pager.getResources().getDimension(R.dimen.folded_label_padding);
    factor = 1 - (textSize + textPadding) / (pager.getWidth());
  }

  @Override
  public AuthFragment getItem(int position) {
    AuthFragment fragment = authArray.get(position);
    if (fragment == null) {
      fragment = position != 1 ? new LogInFragment() : new SignUpFragment();
      authArray.put(position, fragment);
      fragment.setCallback(this);
    }
    return fragment;
  }

  @Override
  public void show(AuthFragment fragment) {
    final int index = authArray.keyAt(authArray.indexOfValue(fragment));
    pager.setCurrentItem(index, true);
    shiftSharedElements(getPageOffsetX(fragment), index == 1);
    for (int jIndex = 0; jIndex < authArray.size(); jIndex++) {
      if (jIndex != index) {
        authArray.get(jIndex).fold();
      }
    }
  }

  private float getPageOffsetX(AuthFragment fragment) {
    int pageWidth = fragment.getView().getWidth();
    return pageWidth - pageWidth * factor;
  }

  private void shiftSharedElements(float pageOffsetX, boolean forward) {
    final Context context = pager.getContext();
    //since we're clipping the page, we have to adjust the shared elements
    AnimatorSet shiftAnimator = new AnimatorSet();
    for (View view : sharedElements) {
      float translationX = forward ? pageOffsetX : -pageOffsetX;
      float temp = view.getWidth() / 3f;
      translationX -= forward ? temp : -temp;
      ObjectAnimator shift = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0, translationX);
      shiftAnimator.playTogether(shift);
    }

    int color = ContextCompat.getColor(context, forward ? R.color.color_logo_sign_up : R.color.color_logo_log_in);
    DrawableCompat.setTint(sharedElements.get(0).getDrawable(), color);
    //scroll the background by x
    int offset = authBackground.getWidth() / 2;
    ObjectAnimator scrollAnimator = ObjectAnimator.ofInt(authBackground, "scrollX", forward ? offset : -offset);
    shiftAnimator.playTogether(scrollAnimator);
    shiftAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
    shiftAnimator.setDuration(pager.getResources().getInteger(R.integer.duration) / 2);
    shiftAnimator.start();
  }

  @Override
  public void scale(boolean hasFocus) {
    final float scale = hasFocus ? 1 : 1.4f;
    final float logoScale = hasFocus ? 0.75f : 1f;
    final View logo = sharedElements.get(0);

    AnimatorSet scaleAnimation = new AnimatorSet();
    scaleAnimation.playTogether(ObjectAnimator.ofFloat(logo, View.SCALE_X, logoScale));
    scaleAnimation.playTogether(ObjectAnimator.ofFloat(logo, View.SCALE_Y, logoScale));
    scaleAnimation.playTogether(ObjectAnimator.ofFloat(authBackground, View.SCALE_X, scale));
    scaleAnimation.playTogether(ObjectAnimator.ofFloat(authBackground, View.SCALE_Y, scale));
    scaleAnimation.setDuration(200);
    scaleAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
    scaleAnimation.start();
  }

  @Override
  public float getPageWidth(int position) {
    return factor;
  }

  @Override
  public int getCount() {
    return 2;
  }
}

مهم ترین بخش این مقاله همینجاست و اگر برای شما نامفهوم است نگران نباشید در ادامه توضیحات کاملی آمده است . 

قبل از هر چیزی ، در ابتدای مقاله یک constructor یا سازنده دیدید. ما به سادگی ، مقدار gap را محاسبه کردیم و زمان جابجایی ViewPager را مشخص کردیم .

 همچنین یک آرایه برای fragment ها ساختیم ، این کار برای بازیابی fragment به همراه موقعیت آن ، عملیات را ساده تر میکند . 

  • show  یک متد هست که وظیفه ی تغییر یک فرگمنت به یک فرگمنت دیگر را دارد . این متد با استفاده از متد ViewPager.setCurrentItem این کار را انجام میدهد 
  • getPageOffsetX یک متد ساده ی کمک کننده است که سایز نوار کناری برای هر فرگمنت را برمیگرداند 
  • shiftSharedElements  برای تنظیم المان های ثابت یا به اشتراک گذاشته شده است 
  • scale  بزرگ نمایی و کوچک نمایی تصویر بگ گراند زمانی که روی یک input مثل ایمیل یا رمز کلیک میکنیم 

همش همینه !

 

صفحات login و Sign Up 

بسیار خب ، وقت آن رسیده است که صفحات اصلی را طراحی کنیم ، بدیهی است که ما فایل های XML مشابهی برای هر فرگمنت خواهیم داشت . در حقیقت ، ما چند قسمت اضافی در صفحه ی ثبت نام و یک TextView  ساده در صفحه ی ورود خواهیم داشت . پس بهتره که ما بجای ایجاد دو فایل جدا یکی بسازیم ! 

برای ادامه ی کار کد زیر را برای صفحه ی ثبت نام در نظر بگیرید البته کدهای XML برای دیزان این صفحه : 

 

sign_up.xml

    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    tools:background="@color/color_sign_up"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

            android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/guideline"
        app:layout_constraintGuide_begin="48dp"
        android:orientation="horizontal" />

            android:id="@+id/logo"
        android:focusable="true"
        android:focusableInTouchMode="true"
        app:layout_constraintTop_toTopOf="@+id/guideline"
        android:layout_marginTop="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_width="64dp"
        android:layout_height="64dp"/>

            style="@style/Widget.TextInputLayout"
        android:id="@+id/email_input"
        android:layout_marginTop="48dp"
        app:layout_constraintTop_toBottomOf="@+id/logo"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">
                    style="@style/Widget.TextEdit"
            android:id="@+id/email_input_edit"
            android:hint="@string/email_hint"
            android:inputType="textEmailAddress" />
   

            style="@style/Widget.TextInputLayout"
        android:id="@+id/password_input"
        app:layout_constraintTop_toBottomOf="@+id/email_input"
        android:layout_marginTop="16dp"
        app:passwordToggleTint="@color/color_input_hint"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">
                    style="@style/Widget.TextEdit"
            android:id="@+id/password_input_edit"
            android:hint="@string/password_hint"
            android:inputType="textPassword" />
   

            style="@style/Widget.TextInputLayout"
        android:id="@+id/confirm_password"
        android:layout_marginTop="16dp"
        app:passwordToggleTint="@color/color_input_hint"
        app:layout_constraintTop_toBottomOf="@+id/password_input"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">
                    style="@style/Widget.TextEdit"
            android:id="@+id/confirm_password_edit"
            android:hint="@string/confirm_hint"
            android:inputType="textPassword"/>
   

            android:id="@+id/caption"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="@dimen/unfolded_size"
        android:textAllCaps="true"
        android:textStyle="bold"
        android:textColor="@color/color_label"
        android:text="@string/sign_up_label"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.78"
        app:layout_constraintRight_toRightOf="parent" />

آموزش ساخت صفحه ی ثبت نام و ورود در اندروید استودیو

خب بعد از اجرای کدهای بالا در یک اکتیویتی میبینید که بصورت خودکار یکی از input ها فوکوس میگیرد و این مسئله باعث میشه که کیبورد اندروید بالا بیاد ! برای جلوگیری از این کار یک View  در ابتدای کدهای بالا ساختیم که فوکوس میگیره و باعث میشه کیبورد همون اول لود شدن ، بالا نیاد !

برای هر input از TextInputEditText استفاده کردیم که در داخل TextInputLayout  قرار گرفته 

برای جلوگیری از کدهای اضافه ، ویژگی های مشترک هر TextInputEditText رو داخل یک فایل style قرار دادم کدهای زیر رو ببینید : 

 

 

   
   

   

   

   

خب ممکنه برای شما هم اندازه ی فونت password  کوچکتر از email  باشه خب برای جلوگیری از این مسئله میتونید کدهای زیر را در متد onCreate  استفاده کنید : 

 

FixViews.java

views.forEach(editText -> {
  if (editText.getId() == R.id.password_input_edit) {
    final TextInputLayout inputLayout = ButterKnife.findById(view, R.id.password_input);
    final TextInputLayout confirmLayout = ButterKnife.findById(view, R.id.confirm_password);
    Typeface boldTypeface = Typeface.defaultFromStyle(Typeface.BOLD);
    inputLayout.setTypeface(boldTypeface);
    confirmLayout.setTypeface(boldTypeface);
    editText.addTextChangedListener(new TextWatcherAdapter() {
      @Override
      public void afterTextChanged(Editable editable) {
        inputLayout.setPasswordVisibilityToggleEnabled(editable.length() > 0);
      }
    });
   }
   editText.setOnFocusChangeListener((temp, hasFocus) -> {
     if (!hasFocus) {
       boolean isEnabled = editText.getText().length() > 0;
       editText.setSelected(isEnabled);
     }
   });
});

توجه کنید که در کد بالا ما از forEach  استفاده کردیم که این متد از api 24 به بعد ساپورت میشود و قبلتر از اون باید از حلقه های ساده استفاده کنید نه forEach !

خب اگر خواستیم حاشیه های input ها گرد نباشد و بصورت مستطیل شکل باشد میتونیم در کد زیر raduis رو به 0dp تغییر بدیم :

      
    
    

 

در زیر یک اسکرین شات میتونید از Login fragment ببینید : 

 آموزش ساخت صفحه ی ثبت نام و ورود در اندروید

هملنطور که میبیند از یک view کاستوم به نام VerticalTextView استفاده کردیم . یک متن بولد شده است که عبارت Log In/Sign Up را نشان میدهد .

چرا نیاز به Textview هست وقتی ما میتوانید از View.setRotation  استفاده کنیم ؟

به این علت که نمیتواند VIEW را بچرخاند بلکه میتواند تنها متن درونی آن را بچرخاند همچنین نمی تواند طول و عرض آن را تغییر دهد و این مسخره به نظر میرسد ! 

تصویر زیر اسکرین شات گرفته شده از موبایل است که مرزهای تصاویر نشان میدهد با وجود عمودی بودن متن ، view همچنان افقی است . این مشکل زمانی رخ میدهد که ما از View.setRotation استفاده کنیم .

آموزش ساخت صفحه ی ثبت نام و ورود در اندروید

نکته این است که این مشکل با استفاده از VerticalTextView حل میشود .

 

اضافه کردن مقداری انیمیشن به پروژه

ما سه نوع انیمیشن استفاده خواهیم کرد : 

  • انیمیشن folding 
  • انیمیشن unfolding
  • انیمیشن scale  زمانی که input ها لمس میشود

Folding animation  : 

 @Override
  public void fold() {
    //release the lock, so you can click on the label again and get the unfold() animation running
    lock = false;
    TransitionSet set = new TransitionSet();
    set.setDuration(getResources().getInteger(R.integer.duration));
    //simple rotate transition
    Rotate transition = new Rotate();
    //at the end of the transiton our view will have -90 angle
    transition.setEndAngle(-90f);
    transition.addTarget(caption);
    //this one animates the translation from the bottom to the middle of the screen
    ChangeBounds changeBounds = new ChangeBounds();
    set.addTransition(changeBounds);
    set.addTransition(transition);
    //size and color animation 
    TextSizeTransition sizeTransition = new TextSizeTransition();
    sizeTransition.addTarget(caption);
    set.addTransition(sizeTransition);
    set.setOrdering(TransitionSet.ORDERING_TOGETHER);
    set.addListener(new Transition.TransitionListenerAdapter() {
      @Override
      public void onTransitionEnd(Transition transition) {
        super.onTransitionEnd(transition);
        caption.setTranslationX(getTextPadding());
        caption.setRotation(0);
        caption.setVerticalText(true);
        caption.requestLayout();
      }
    });
    TransitionManager.beginDelayedTransition(parent, set);
    //this is 20 sp
    caption.setTextSize(TypedValue.COMPLEX_UNIT_PX, caption.getTextSize() / 2f);
    //change color to super white
    caption.setTextColor(Color.WHITE);
    ConstraintLayout.LayoutParams params = getParams();
    //release the right constraint, so the view gets translated to the left
    params.rightToRight = ConstraintLayout.LayoutParams.UNSET;
    //view is positioned in the center of the screen
    params.verticalBias = 0.5f;
    caption.setLayoutParams(params);
    caption.setTranslationX(-caption.getWidth() / 8 + getTextPadding());
  }

 

لینک کوتاه این مقاله : https://avasam.ir/post/74
این سیستم برپایه ی علاقه مندی شما یک دوره ی مناسب به شما پیشنهاد میدهد
مرا بسوی بهترین دوره ی آموزشی که برای من مناسب است هدایت کن 🤖
برای استفاده ی دیگران و حمایت از ما در جامعه های زیر به اشتراک بگذارید

.:: نظرهای کاربران ::.
سوفیا / 7 ماه پیش

چرا عکس ها لود نمیشن میشه اپدیت کنید ععکس ها بیاد ببینیم خروجی چی میشه؟!

پشتیبانی آواسام :

سلام ممنون از گزارشتون بررسی میکنیم و تصاویر را درست میکنیم. با احترام

نازنین / 11 ماه پیش

سلام مهندس نیکزاد عزیز آموزش های شما عالیه ولی این نوشته فک کنم قدیمی شده باید بروزرسانیش کنید تصاویر لود نمیشه لطفا رسیدگی کنید ما به آموزش های شما نیاز داریم

رامین مرادی / 5 سال پیش
تصاویر لود نمیشن.!!!!
امید غلامی / 6 سال پیش
کاش چند تا اسکرین شات هم گذاشته بودید که قبل از کارکردن متوجه بودیم که قرار چه نتیجه ای به دست بیاد
رامین مرادی / 6 سال پیش
تصاویر لود نمیشن!!
کدنویس آواسام 21 / 6 سال پیش
سیبیسب
پشتیبانی آواسام :
سیبیسب
دوره ی آموزش پروژه محور ساخت کافه بازار دوره ی آموزش پروژه محور ساخت فروشگاه دیجیکالا آموزش لاراول دوره ی آموزش ویو جی اس