ارتباط بین فرگمنت ها با استفاده از ViewModel


پیاده سازی ارتباط بین فرگمنت ها با استفاده از ViewModel

ارتباط بین فرگمنت ها در غالب برنامه ها ضروری ست. برای این منظور روش های متعددی وجود دارد. یکی از این روش ها استفاده از ViewModel است. امروز به پیاده سازی ارتباط بین فرگمنت ها با استفاده از ViewModel می پردازیم. توجه داشته باشید که ارتباط بین فرگمنت ها با استفاده از ViewModel، روش پیشنهادی گوگل می باشد. در این روش، یک نمونه از ViewModel که مطعلق به اکتیویتی ست، می سازیم. و این object بین فرگمنت ها به اشتراک گذاشته می شود.

اما VieeModel چیست؟

در واقع component های مربوط به معماری اندروید، ViewModel را به عنوان یک کلاس کمکی ارائه دادند. ViewModel به منظور برای نگهداری و مدیریت وضعیت UI استفاده می شود. ViewModel ها بسیار به چرخه حیات حساس هستند. یک اکتیویتی همیشه می تواند یک ViewModel داشته باشد. هر اکتیویتی می تواند چند فرگمنت داشته باشد. همه فرگمنت های مربوط به اکتیویتی از یک ViewModel مشترک استفاده می کنند.

تبادل داده میان فرگمنت ها

در سناریوهای مختلف برای برنامه ها همیشه ارتباط میان فرگمنت ها وجود دارد. برای این منظور روش های گوناگونی وجود دارد. یکی از این روش های برای ارتباط بین فرگمنت ها استفاده از ViewModel می باشد. در این مقاله برای پیاده سازی این ارتباط از ViewModel استفاده می کنیم. برای آشنایی هرچه بهتر با ViewModel می توانید به این مقاله مراجعه کنید. هم چنین روش های استفاده بهینه از ViewModel برای جلوگیری از memory leak را در این مقاله مطالعه نمایید.

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

قدم اول : ایجاد پروژه و افزودن dependency های مربوطه

dependencies {
  
  implementation 'androidx.appcompat:appcompat:1.0.2'
  implementation 'com.google.android.material:material:1.0.0'
  implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
  implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
  implementation 'androidx.legacy:legacy-support-v4:1.0.0'
}

قدم : آماده سازی UI مربوط به MainActivity

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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=".MainActivity"
    >
  <com.google.android.material.appbar.AppBarLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:theme="@style/AppTheme.AppBarOverlay"
      >
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minHeight="?actionBarSize"
        android:padding="@dimen/appbar_padding"
        android:text="@string/app_name"
        android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
        />
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        />
  </com.google.android.material.appbar.AppBarLayout>
  <androidx.viewpager.widget.ViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"
      />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

قدم سوم : ساخت زیر کلاس برای ViewModel

یک نمونه از <MutableLiveData<String می سازیم. این نمونه ورودی را از فرگمنت مبدا ذخیره می کند و به فرگمنت مقصد تحویل می دهد.

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
public class PageViewModel extends ViewModel {
  /**
   * Live Data Instance
   */
  private MutableLiveData<String> mName = new MutableLiveData<>();
  public void setName(String name) {
    mName.setValue(name);
  }
  public LiveData<String> getName() {
    return mName;
  }
}

قدم چهارم : ساخت فرگمنت مبدا با نام FirstFragment

ابتدا layout مربوط به FirstFragment را می سازیم :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    >
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="72dp"
      android:layout_height="72dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="24dp"
      android:src="@drawable/user_avatar"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
  <com.google.android.material.textfield.TextInputLayout
      android:id="@+id/textInputLayout"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="16dp"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="32dp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/imageView"
      style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
      >
    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/textInputTextName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Name"
        />
  </com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

سپس این UI را به کلاس فرگمنت متصل نموده و ViewModel را مقداردهی اولیه می کنیم:

// init ViewModel
pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);

پس از مقداردهی اولیه داده مربوطه را به آن تزریق می نماییم:

pageViewModel.setName(charSequence.toString());

و در نهایت کد مربوط به FirstFragment به شکل زیر خواهد بود:

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import com.fragmentcommunicationexample.R;
import com.google.android.material.textfield.TextInputEditText;
public class FirstFragment extends Fragment {
  private PageViewModel pageViewModel;
  public FirstFragment() {
    // Required empty public constructor
  }
  /**
   * Create a new instance of this fragment
   * @return A new instance of fragment FirstFragment.
   */
  public static FirstFragment newInstance() {
    return new FirstFragment();
  }
  @Override public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // init ViewModel
    pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);
  }
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_first, container, false);
  }
  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    TextInputEditText nameEditText = view.findViewById(R.id.textInputTextName);
    // Add Text Watcher on name input text
    nameEditText.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) {
        pageViewModel.setName(charSequence.toString());
      }
      @Override public void afterTextChanged(Editable editable) {
      }
    });
  }
}

قدم پنجم : ساخت فرگمنت مقصد

مثل مرحله قبل ابتدا UI مربوط به فرگمنت SecondFragment را می سازیم:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".ui.main.SecondFragment"
    >
  <ImageView
      android:id="@+id/imageView2"
      android:layout_width="72dp"
      android:layout_height="72dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="24dp"
      android:src="@drawable/user_avatar"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      />
  <TextView
      android:id="@+id/textViewName"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="32dp"
      android:gravity="center"
      android:hint="User Display Name"
      android:textColor="@color/colorPrimaryDark"
      android:textSize="22sp"
      android:textStyle="bold"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/imageView2"
      tools:text="Morris"
      />
</androidx.constraintlayout.widget.ConstraintLayout>

در نهایت پس از کشیدن layout این فرگمنت تنها یک کار باقی می ماند. آن هم نمایش دادن داده ای ست که از فرگمنت مبدا دریافت شده.

pageViewModel.getName().observe(requireActivity(), new Observer<String>() {
    @Override
    public void onChanged(@Nullable String s) {
      txtName.setText(s);
    }
  });

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

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.fragmentcommunicationexample.R;
public class SecondFragment extends Fragment {
  private PageViewModel pageViewModel;
  private TextView txtName;
  public ≈() {
    // Required empty public constructor
  }
  /**
   * Use this factory method to create a new instance of this fragment.
   *
   * @return A new instance of fragment SecondFragment.
   */
  public static SecondFragment newInstance() {
    return new SecondFragment();
  }
  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // initialise ViewModel here
    pageViewModel = ViewModelProviders.of(requireActivity()).get(PageViewModel.class);
  }
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_second, container, false);
  }
  @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    txtName = view.findViewById(R.id.textViewName);
    pageViewModel.getName().observe(requireActivity(), new Observer<String>() {
      @Override
      public void onChanged(@Nullable String s) {
        txtName.setText(s);
      }
    });
  }
}

قدم ششم : ساخت زیر کلاسی از FragmentPagerAdapter

همان طور که در نمایش دموی پروژه مشاهده نمودید، از ViewPager استفاده کرده ایم. پس نیاز به ساخت زیر کلاسی از FragmentPagerAdapter داریم.

import android.content.Context;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.fragmentcommunicationexample.R;
/**
 * A [FragmentPagerAdapter] that returns a fragment corresponding to
 * one of the sections/tabs/pages.
 */
public class ViewPagerAdapter extends FragmentPagerAdapter {
  @StringRes
  private static final int[] TAB_TITLES = new int[] { R.string.tab_text_1, R.string.tab_text_2 };
  private final Context mContext;
  public ViewPagerAdapter(Context context, FragmentManager fm) {
    super(fm);
    mContext = context;
  }
  @Override
  public Fragment getItem(int position) {
    // getItem is called to instantiate the fragment for the given page.
    if (position == 0) {
      return FirstFragment.newInstance();
    } else {
      return SecondFragment.newInstance();
    }
  }
  @Nullable
  @Override
  public CharSequence getPageTitle(int position) {
    return mContext.getResources().getString(TAB_TITLES[position]);
  }
  @Override
  public int getCount() {
    // Show 2 total pages.
    return 2;
  }
}

قدم نهایی : تکمیل نمودن کلاس اکتیویتی

در آخر به سراغ MainActivity رفته و مثل کد زیر آن را کامل می کنیم:

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import com.fragmentcommunicationexample.ui.main.ViewPagerAdapter;
import com.google.android.material.tabs.TabLayout;
public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPagerAdapter viewPagerAdapter =
        new ViewPagerAdapter(this, getSupportFragmentManager());
    ViewPager viewPager = findViewById(R.id.view_pager);
    viewPager.setAdapter(viewPagerAdapter);
    TabLayout tabs = findViewById(R.id.tabs);
    tabs.setupWithViewPager(viewPager);
  }
}

 

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز 3 / 5. تعداد: 2

از امتیاز شما متشکریم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image