معماری MVVM با تزریق وابستگی Hilt, RxJava3, Live Dataو View Binding در اندروید

معماری MVVM با تزریق وابستگی Hilt, RxJava3, Live Dataو View Binding در اندروید

برای دانلود سورس اینجا کلیک کنید

در این مقاله ، نحوه اجرای معماریMVVMباHilt،RxJava،Retrofit،Room،Live DataوView Bindingرا خواهیم دید.

این مقاله برای شخصی است که میخواهد

  • تزریق وابستگی جدید با استفاده ازHilt
  • مهاجرت بهHiltاز داگر

را یاد بگیرد.

در این پروژه ، جزئیات که یکAPI RESTتشکیل می دهد را به دست می آوریم ،و سپس ازRoomبرای ذخیره آن به صورت آفلاین استفاده می کنیم. از آنجا که تعدادی مفاهیم در اینجا مورد استفاده قرار می گیرند، به تک تک آنها خواهیم پرداخت.

ساختار پروژه:

ابتدا تمام وابستگی های لازم را به پروژه خود اضافه کنید.

apply plugin: 'com.android.application'
apply plugin: 'dagger.hilt.android.plugin'


android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.example.pokemon"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        multiDexEnabled true

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }

    viewBinding {
        enabled = true
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    def room_version = "2.2.5"
    def nav_version = "2.3.0-beta01"

    implementation "androidx.recyclerview:recyclerview:1.1.0"
    implementation "androidx.cardview:cardview:1.0.0"
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'com.android.support:multidex:1.0.3'

    // Hilt
    implementation "com.google.dagger:hilt-android:2.28-alpha"
    annotationProcessor 'com.google.dagger:hilt-android-compiler:2.28-alpha'
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
    annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'

    //RxJava
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'io.reactivex.rxjava3:rxjava:3.0.0'

    // Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation "com.github.akarnokd:rxjava3-retrofit-adapter:3.0.0"

    // ViewModel
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'

    // LiveData
    implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'

    // Room
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"

    // Navigation
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-ui:$nav_version"

    // Glide
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'


    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
 

تنظیمApi Service

برای واکشی جزئیات در اینجا ازPokeApiاستفاده می کنیم.

public interface PokeApiService {

    @GET("pokemon")
    Observable<PokemonResponse> getPokemons();
}

همانطور که می بینید نوع بازگشتObservableاست ،  در موردRxJavaاطلاعات بعدی را بعداً در مقاله مشاهده خواهید کرد.

کلاس مدلPokemonوPokemonResponseما به این شکل است:

public class Pokemon {

    private int id;
    private String name;

    private String url;

    public Pokemon(String name, String url) {
        this.name = name;
        this.url = url;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

 

 

public class PokemonResponse {
    private Integer count;
    private String next,previous;
    private ArrayList<Pokemon> results;

    public PokemonResponse(Integer count, String next, String previous, ArrayList<Pokemon> results) {
        this.count = count;
        this.next = next;
        this.previous = previous;
        this.results = results;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public String getNext() {
        return next;
    }

    public void setNext(String next) {
        this.next = next;
    }

    public String getPrevious() {
        return previous;
    }

    public void setPrevious(String previous) {
        this.previous = previous;
    }

    public ArrayList<Pokemon> getResults() {
        return results;
    }

    public void setResults(ArrayList<Pokemon> results) {
        this.results = results;
    }
}

 

تنظیمHilt

کلاسBaseApplicationاین کلاس برایHiltلازم است و باید انوتیشین@HiltAndroidAppرا برای آن بنویسیم فراموش نکنید که این کلاس را بهmanifestاضافه کنید

<application
    android:name=".BaseApplication"

package com.example.pokemon;

import android.app.Application;

import dagger.hilt.android.HiltAndroidApp;


@HiltAndroidApp
public class BaseApplication extends Application {
}

 

اکنون ماژولNetworkرا ایجاد خواهیم کرد.

حالا ماژول چیست؟

ماژول کلاسی است که اطلاعاتی را در مورد چگونگی تهیهinstanceاز کلاس که ما از آن برخوردار نیستیم ، در اختیارمان قرار می دهد. برای اینکه بتوانیم درHiltاز اشیاء استفاده کنیم  از انوتیشنInject@ بالاتر از سازنده کلاس استفاده می کنیم اما هنگامی که ما یک کلاس نداریم یا وقتی با یکinterfaceکار می کنیم که سازنده ندارد ، کجا می توانیم انوتیشن@injectرا قرار دهیم. در این موارد ما از انوتیشنProvide@ در داخل کلاس ماژول استفاده می کنیم تا بهhiltبگوییم که اینobject ها را برای ما بسازد.

برای راه اندازی ماژول یک کلاسNetworkModuleو حاشیه نویسی آن با انوتیشنModule@ اکنون باید یک انوتیشن  دیگر اضافه کنیم که انوتیشنInsatallIn@ است و ماApplicationComponent  را به آن پاس میدهیم زیرا ما می خواهیمNetworkModuleبرایapplication scopeدر دسترس ما باشد.در ماژول متدی را برای دریافت شیءPokeApiServiceارائه خواهیم داد. یک متدPokeApiServiceاز نوع بازگشتPokeApiServiceایجاد کنید و آن را با انوتیشنProvide@ بنویسید.

@Module
@InstallIn(ApplicationComponent.class)
public class NetworkModule {

    @Provides
    @Singleton
    public static PokeApiService providePokemonApiService(){

        return  new Retrofit.Builder()
                .baseUrl(" https://pokeapi.co/api/v2/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .build()
                .create(PokeApiService.class);
    }
}

برنامه ما دوfragment دارد که یکی از آنها لیستpokemonگرفته شده ازAPIرا نشان میدهد وfragmentدومpokemonمورد علاقه خود را که با استفاده ازRoomذخیره کرده ایم نشان می دهد و ما از دکمه ای برای جابجایی بین اینfragmentها استفاده خواهیم کرد.

 

تنظیماتRepository

 

public class Repository {

    private PokeDao pokeDao;
    private PokeApiService apiService;

    @Inject
    public Repository(PokeDao pokeDao, PokeApiService apiService) {
        this.pokeDao = pokeDao;
        this.apiService = apiService;
    }


    public Observable<PokemonResponse> getPokemons(){
        return apiService.getPokemons();
    }
}

ما ازInject@ در بالای سازندهRepositoryاستفاده کرده ایم تا هر زمان که به یک شیRepositoryاحتیاج داشته باشیم ، یکRepositoryرا برای ما فراهم می کند. کلاسRepositoryدارای دو وابستگیPokeApiServiceوPokeDaoاست.

PokeDaoمربوط بهRoomاست که آن را نادیده می گیرد و اکنون در مقاله در مورد آن صحبت خواهیم کرد.

نکته مهم این است که اگر ما می خواهیمhiltبه ماRepositoryرا ارائه دهد ، ما همچنین باید بگوییم که چگونه نمونه ای از کلاس ها یاinterfaceهایی را که کلاسRepositoryما به آنها وابستگی دارد ارائه دهیم. ما قبلاًNetworkModuleرا ایجاد کرده ایم که متدی را برای گرفتن یک شیءPokeApiServiceارائه می دهد. بنابر این هنگامی که ماRepositoryرا در اختیار داریمHilt به صورت خودکار تمام وابستگی های کلاسRepositoryرا تامین میکند. ما به سادگی یک متدgetPokemonsایجاد کرده ایم تاObservableاز نوعPokemonResponseرا بازگردانیم.

 

تنظیماتViewModel

 

public class PokemonViewModel extends ViewModel {
    private static final String TAG = "PokemonViewModel";

    private Repository repository;
    private MutableLiveData<ArrayList<Pokemon>> pokemonList = new MutableLiveData<>();
    private LiveData<List<Pokemon>> favoritePokemonList = null;

    @ViewModelInject
    public PokemonViewModel(Repository repository) {
        this.repository = repository;
        favoritePokemonList = repository.getFavoritePokemon();
    }

    public MutableLiveData<ArrayList<Pokemon>> getPokemonList() {
        return pokemonList;
    }

    public void getPokemons(){
        repository.getPokemons()
                .subscribeOn(Schedulers.io())
                .map(new Function<PokemonResponse, ArrayList<Pokemon>>() {
                    @Override
                    public ArrayList<Pokemon> apply(PokemonResponse pokemonResponse) throws Throwable {
                        ArrayList<Pokemon> list = pokemonResponse.getResults();
                        for(Pokemon pokemon : list){
                            String url = pokemon.getUrl();
                            String[] pokemonIndex = url.split("/");
                            pokemon.setUrl("https://pokeres.bastionbot.org/images/pokemon/"+pokemonIndex[pokemonIndex.length-1] +".png");
                        }
                        Log.e(TAG, "apply: "+list.get(2).getUrl());
                        return list;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(result -> pokemonList.setValue(result),
                        error-> Log.e(TAG, "getPokemons: " + error.getMessage() ));
    }
}

در اینجا ما انوتیشن جدیدViewModelInjectرا مشاهده می کنیم و به من اعتماد کنید بهترین کار در مورد استفاده ازHiltاست. اگر در پروژه های خود باMVVMازDaggerاستفاده کرده اید ، باید با مشکل تزریقViewModelها روبرو شده باشید. معروف ترین راه حل ایجاد کلاسViewModelFactoryبود اما این کار طولانی و خسته کننده بود. اما باHilt، انوتیشن تمام کارها را انجام می دهد و به راحتی می توانیدViewModelsرا تزریق کنید. در اینجا ما یک متدgetPokemonsایجاد کرده ایم. همانطور که می دانیمrepository.getPokemons() یکObservableرا برمی گرداند ما آن راobserveمی کنیم و سپسpokemonList(Mutable Live Data) را تنظیم می کنیم تاfragmentتغییرات را به روز کند.

RxJavaدارای سه مفهوم اساسیObservable،ObserversوOperatorsاست.

observableبه عنوانentityبرای برخی از داده های منتشر شده قابل مشاهده است اما اگر کسی درobservable  آن نباشد ، چه فایده ای دارد از این روobserveکنید ، بنابراین ماObserverهایی داریم که داده های منتشر شده توسطObservableها را مشاهده می کنند. اپراتورها چیزی است که داده ها را دستکاری می کند و یا داده ها را دگرگون می کند و آن را بهsubscribersها می دهد.

SubscribOn (Schedulers.io ())به این معنی است که می خواهیم درthreadبکگراندsubscribeشویم وobserveOn(AndroidSchedulers.mainThread())به این معنی است که می خواهیم داده ها را درthreadاصلی مشاهده کنیم.

در اینجا از اپراتورmapاستفاده می شود ، زیرا ما می خواهیمPokemon Listرا دریافت کنیم ، اما ما یکObservableازPokemonReosponseرا دریافت می کنیم تا بتوانیم لیستpokemonرا از  pokemonResponse.getResults()بگیریم.

اما ما همچنین می خواهیم یک چیز دیگر را تغییر دهیم. URLای که ازPokemon Objectدریافت می کنیم حاوی جزئیاتی دربارهpokemonاست اما ما فقط تصویرpokemonرا می خواهیم بنابراین نمایهpokemonرا از اینURLدریافت می کنیم و سپس آن را باURLهای مختلف به هم می زنیم که مستقیماً آدرس تصویرpokemonرا در اختیار ما قرار می دهد.

تنظیماتRoom

برای تنظیمRoom، ما به سه چیزEntity،DaoوDatabaseنیاز داریم.

برایEntity، کلاسPokemon Pojoرا مانند زیر به روز خواهیم کرد

@Entity(tableName = "favorite_table")
public class Pokemon {

    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;

    private String url;

    public Pokemon(String name, String url) {
        this.name = name;
        this.url = url;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

 

برایDao، ما یکInterfaceبه اسمPokeDaoبه شرح زیر ایجاد خواهیم کرد:

 

@Dao
public interface PokeDao {

    @Insert
    void insertPokemon(Pokemon pokemon);

    @Query("DELETE FROM favorite_table WHERE name = :pokemonName")
    void deletePokemon(String pokemonName);

    @Query("DELETE FROM favorite_table")
    void deleteAll();

    @Query("SELECT * FROM favorite_table")
    LiveData<List<Pokemon>> getFavoritePokemons();
}

ما متد هایی را برای قرار دادنPokemonدر لیست علاقه مندی های ما ایجاد کرده ایم ،pokemonرا از لیست حذف کرده و همه پوکمون ها را از لیست دریافت می کنیم

اکنون ما برای ذخیرهpokemonمورد علاقه هایمان کلاس پایگاه دادهPokemonDBکه از نوعabstractهست را ایجاد می کنیم.

@Database(entities = {Pokemon.class},version = 2,exportSchema = false)
public abstract class PokemonDB extends RoomDatabase {
        public abstract PokeDao pokeDao();
}

ما یک متدabstractاز نوع بازگشتPokeDaoاضافه کرده ایم.

اکنونDatabaseModuleرا ایجاد خواهیم کرد که در صورت داشتن این پایگاه داده به نمونه سازی کمک می کند.

@Module
@InstallIn(ApplicationComponent.class)
public class DataBaseModule {

    @Provides
    @Singleton
    public static PokemonDB providePokemonDB(Application application){
         return Room.databaseBuilder(application,PokemonDB.class,"Favorite Database")
                 .fallbackToDestructiveMigration()
                 .allowMainThreadQueries()
                 .build();
    }

    @Provides
    @Singleton
    public static PokeDao providePokeDao(PokemonDB pokemonDB){
        return pokemonDB.pokeDao();
    }
}

ما انوتیشن های@Moduleو@InstallIn را در بالای این کلاس اضافه کرده ایم تا بهHiltبگوییم این یک ماژول است و برایapplication scopeلازم است.

@Singletonبرای داشتن یک  نمونه واحد(instance single) از این بانک اطلاعاتی در کل برنامه استفاده می شود. اکنون کلاسRepositoryو کلاسPokemonViewModelرا اصلاح خواهیم کرد.

public class Repository {

    private PokeDao pokeDao;
    private PokeApiService apiService;

    @Inject
    public Repository(PokeDao pokeDao, PokeApiService apiService) {
        this.pokeDao = pokeDao;
        this.apiService = apiService;
    }


    public Observable<PokemonResponse> getPokemons(){
        return apiService.getPokemons();
    }

    public void insertPokemon(Pokemon pokemon){
        pokeDao.insertPokemon(pokemon);
    }

    public void deletePokemon(String pokemonName){
        pokeDao.deletePokemon(pokemonName);
    }

    public void deleteAll(){
        pokeDao.deleteAll();
    }

    public LiveData<List<Pokemon>> getFavoritePokemon(){
        return pokeDao.getFavoritePokemons();
    }
}

 

public class PokemonViewModel extends ViewModel {
    private static final String TAG = "PokemonViewModel";

    private Repository repository;
    private MutableLiveData<ArrayList<Pokemon>> pokemonList = new MutableLiveData<>();
    private LiveData<List<Pokemon>> favoritePokemonList = null;

    @ViewModelInject
    public PokemonViewModel(Repository repository) {
        this.repository = repository;
        favoritePokemonList = repository.getFavoritePokemon();
    }

    public MutableLiveData<ArrayList<Pokemon>> getPokemonList() {
        return pokemonList;
    }

    public void getPokemons(){
        repository.getPokemons()
                .subscribeOn(Schedulers.io())
                .map(new Function<PokemonResponse, ArrayList<Pokemon>>() {
                    @Override
                    public ArrayList<Pokemon> apply(PokemonResponse pokemonResponse) throws Throwable {
                        ArrayList<Pokemon> list = pokemonResponse.getResults();
                        for(Pokemon pokemon : list){
                            String url = pokemon.getUrl();
                            String[] pokemonIndex = url.split("/");
                            pokemon.setUrl("https://pokeres.bastionbot.org/images/pokemon/"+pokemonIndex[pokemonIndex.length-1] +".png");
                        }
                        Log.e(TAG, "apply: "+list.get(2).getUrl());
                        return list;
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(result -> pokemonList.setValue(result),
                        error-> Log.e(TAG, "getPokemons: " + error.getMessage() ));
    }

    public void insertPokemon(Pokemon pokemon){
        repository.insertPokemon(pokemon);
    }
    public void deletePokemon(String pokemonName){
        repository.deletePokemon(pokemonName);
    }

    public LiveData<List<Pokemon>> getFavoritePokemonList() {
        return favoritePokemonList;
    }

    public void getFavoritePokemon(){
       favoritePokemonList = repository.getFavoritePokemon();
    }



}

تنظیماتRoom،HiltوViewModelرا به پایان رسانده ایم. اکنونFragmentوActivityرا تنظیم خواهیم کرد.

 

تنطیماتFragmentوActivity

فرگمنتHome

@AndroidEntryPoint
public class Home extends Fragment {
    private static final String TAG = "Home";
    private HomeBinding binding;
    private PokemonViewModel viewModel;
    private PokemonAdapter adapter;
    private ArrayList<Pokemon> pokemonList;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = HomeBinding.inflate(inflater,container,false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        viewModel = new ViewModelProvider(this).get(PokemonViewModel.class);

        initRecyclerView();
        observeData();
        setUpItemTouchHelper();
        viewModel.getPokemons();
    }

    private void setUpItemTouchHelper() {
        ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                int swipedPokemonPosition = viewHolder.getAdapterPosition();
                Pokemon pokemon = adapter.getPokemonAt(swipedPokemonPosition);
                viewModel.insertPokemon(pokemon);
                adapter.notifyDataSetChanged();
                Toast.makeText(getContext(),"Pokemon added to favorites.",Toast.LENGTH_SHORT).show();
            }
        };

        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
        itemTouchHelper.attachToRecyclerView(binding.pokemonRecyclerView);
    }


    private void observeData() {
        viewModel.getPokemonList().observe(getViewLifecycleOwner(), new Observer<ArrayList<Pokemon>>() {
            @Override
            public void onChanged(ArrayList<Pokemon> pokemons) {
                Log.e(TAG, "onChanged: " + pokemons.size() );
                adapter.updateList(pokemons);
            }
        });
    }

    private void initRecyclerView() {
        binding.pokemonRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        adapter = new PokemonAdapter(getContext(),pokemonList);
        binding.pokemonRecyclerView.setAdapter(adapter);
    }
}

ما اینfragmentرا با انوتیشنAndroidEntryPointنوشته ایم ، بدین معنی کهHiltباید تمام وابستگی های اینfragmentرا که می خواهد ارائه دهد.

نکته مهمی که باید به آن توجه داشت:

اگر کلاسAndroidرا با انوتیشنAndroidEntryPointمینویسید، باید کلاسهایAndroidکه به آن بستگی دارد را با همین انوتیشن بنویسید. به عنوان مثال ، اگر یکfragmentرا با این انوتیشن مینویسید ، پس از آن بایدActivityهایی را که در آن قسمت استفاده می کنید با این انوتیشن بنویسید.

ما از کلاسItemTouchHelperبرای عملکردswiping functionاستفاده کرده ایم و می خواهیم وقتی یک آیتم از لیست را تاچ کردیم و به سمت راست کشیدیم  pokemonرا به موارد دلخواه اضافه کند و با کشیدن انگشت به سمت چپpokemonمورد علاقه را حذف کرده و آنها را از لیست موارد دلخواه حذف کند.

فرگمنتFavorites

@AndroidEntryPoint
public class Favorites extends Fragment {
    private FavoritesBinding binding;
    private PokemonViewModel viewModel;
    private PokemonAdapter adapter;
    private ArrayList<Pokemon> pokemonList;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FavoritesBinding.inflate(inflater,container,false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        viewModel = new ViewModelProvider(this).get(PokemonViewModel.class);

        initRecyclerView();
        setUpItemTouchHelper();
        observeData();
        //viewModel.getFavoritePokemon();
    }

    private void observeData() {
        viewModel.getFavoritePokemonList().observe(getViewLifecycleOwner(), new Observer<List<Pokemon>>() {
            @Override
            public void onChanged(List<Pokemon> pokemons) {

                if(pokemons == null || pokemons.size() == 0)
                    binding.noFavoritesText.setVisibility(View.VISIBLE);
                else{
                    ArrayList<Pokemon> list = new ArrayList<>();
                    list.addAll(pokemons);
                    adapter.updateList(list);
                }
            }
        });
    }

    private void setUpItemTouchHelper() {
        ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.LEFT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                int swipedPokemonPosition = viewHolder.getAdapterPosition();
                Pokemon pokemon = adapter.getPokemonAt(swipedPokemonPosition);
                viewModel.deletePokemon(pokemon.getName());
                Toast.makeText(getContext(),"Pokemon removed from favorites.",Toast.LENGTH_SHORT).show();
            }
        };

        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
        itemTouchHelper.attachToRecyclerView(binding.favoritesRecyclerView);
    }


    private void initRecyclerView() {
        binding.favoritesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        adapter = new PokemonAdapter(getContext(),pokemonList);
        binding.favoritesRecyclerView.setAdapter(adapter);
    }

}

ما همان کارهایی را انجام داده ایم که در فرگمنتHomeانجام داده ایم و فقط تغییر جهت را در آیتمTouchHelperتغییر می دهیم.

Main Activity

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private boolean isFavoriteListVisible = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        getSupportFragmentManager().beginTransaction().replace(R.id.frameLayout,new Home())
                .commit();

        binding.changeFragment.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(isFavoriteListVisible){
                  isFavoriteListVisible = false;
                  binding.changeFragment.setText("Favorites");
                  getSupportFragmentManager().beginTransaction().replace(R.id.frameLayout,new Home())
                          .commit();
                }
                else {
                    isFavoriteListVisible = true;
                    binding.changeFragment.setText("Home");
                    getSupportFragmentManager().beginTransaction().replace(R.id.frameLayout,new Favorites())
                            .commit();
                }
            }
        });
    }
}

درActivity، از دکمه برای تغییرfragment ها استفاده کرده ایم و ازviewbindingهم استفاده کرده ایم.

جدیدترین ویدئوهای آموزشی

در بخش TV باگتو، آموزش های کوتاه و جدید را مشاهده نمایید

0 نظرات

برای ارسال نظر باید وارد حساب کاربری خود شوید
ورود به حساب کاربری ثبت نام

x "🌹🪴 نوروزتان پیروز 🌱🌷💐فروش ویژه نوروزی در تاریخ 6ام تا 8ام فروردین 🌻😍🌹"