1

У меня есть активность. В Activity находится ViewPager с вкладками, каждая вкладка содержит фрагмент. У самих фрагментов есть RecyclerView каждый. Мне нужно передать изменения из адаптера RecyclerView в действие.Коммуникация между компонентами в Android

В настоящее время я использую шаблон слушателя и общаюсь с использованием интерфейса между каждым из компонентов. Т.е. у меня есть интерфейс между адаптером RecyclerView и фрагментом, удерживающим его. Затем интерфейс из фрагмента в FragmentStatePagerAdapter ViewPager, который создает все фрагменты. И еще 1 интерфейс между адаптером ViewPager и Activity, в котором находится ViewPager. Я чувствую, что слишком много интерфейсов для всех компонентов из-за того, как они структурированы.

В настоящее время я не сталкиваюсь с проблемами как таковыми, но я думаю, что шаблон слушателя действует как анти-шаблон из-за всех вложенных компонентов. Вместо создания независимых компонентов, я думаю, что иерархия затруднит внесение изменений кода в будущем.

Я делаю это правильно или есть лучший способ сделать это? Является ли это случаем, когда я должен использовать шину событий или шаблон наблюдателя (если да, то можете ли вы указать мне некоторые примеры, когда кто-то преодолел подобные проблемы, используя его)?

ПРИМЕЧАНИЕ. Если это имеет значение, мне нужно, чтобы он поддерживал глобальный объект в активности, что-то вроде корзины покупок, где я могу добавлять или удалять элементы, и эти элементы присутствуют в адаптере RecyclerView, откуда я могу добавить его в а также увеличивать или уменьшать счетчик для определенного элемента. ViewPager и вкладки помогают разделять эти элементы в разных категориях.

Редактировать 1: Некоторый код пытается @ подход LucaNicoletti - Я пропустил один уровень, который является уровнем с помощью параметра FragmentStatePagerAdapter ViewPager. Думаю, это не имеет значения и лишено какого-либо другого кода, чтобы он был маленьким.

MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener, FoodAdapter.OnFoodItemCountChangeListener { 

    @Override 
    public void onFoodItemDecreased(FoodItemModel foodItemModel, int count) { 
     Log.d("Test", "Dec"); 
    } 

    @Override 
    public void onFoodItemIncreased(FoodItemModel foodItemModel, int count) { 
     Log.d("Test", "Inc"); 
    } 
    // Other methods here 
} 

Фрагмент хостинг адаптер:

public class FoodCategoryListFragment extends Fragment implements FoodAdapter.OnFoodItemCountChangeListener { 

    // Other boring variables like recyclerview and layout managers 
    FoodAdapter foodAdapter; 


    @Override 
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 
     super.onViewCreated(view, savedInstanceState); 
     // Other boring intializations for recyclerview and stuff 

     // I set the click listener here directly on the adapter instance 
     // I don't have this adapter instance in my activity 
     foodAdapter.setOnFoodItemClickListener(this); 
     rvFoodList.setAdapter(foodAdapter); 
    } 
} 

Класс адаптера на самом низком уровне:

public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.FoodViewHolder> { 

    private OnFoodItemCountChangeListener onFoodItemCountChangeListener; 
    private List<FoodItemModel> foodItems; 

    // The interface 
    public interface OnFoodItemCountChangeListener { 
     void onFoodItemIncreased(FoodItemModel foodItemModel, int count); 

     void onFoodItemDecreased(FoodItemModel foodItemModel, int count); 
    } 

    // This is called from the fragment since I don't have the adapter instance 
    // in my activty 
    public void setOnFoodItemClickListener(OnFoodItemCountChangeListener onFoodItemCountChangeListener) { 
     this.onFoodItemCountChangeListener = onFoodItemCountChangeListener; 
    } 

    // Other boring adapter stuff here 
@Override 
     public void onClick(View view) { 
      switch (view.getId()) { 
       case R.id.bMinus: 
         onFoodItemCountChangeListener.onFoodItemDecreased(foodItems.get(getAdapterPosition()), 
           Integer.parseInt(etCounter.getText().toString())); 
        } 
        break; 
       case R.id.bPlus: 

         onFoodItemCountChangeListener.onFoodItemIncreased(foodItems.get(getAdapterPosition()), 
           Integer.parseInt(etCounter.getText().toString())); 
        } 
        break; 
      } 
     } 
} 
+0

Если слушатели в середине просто направить действия на более высокий уровень, удалить интерфейс к этому уровню, и использовать только выше один –

+0

то, что вы должны/могли бы сделать это, чтобы иметь глобальный репозиторий данных, который держит корзины покупок и слушателей, связанных с изменениями в нем. Как и singleton, например 'ShoppingCart.getInstance(). AddListener (this);' и 'ShoppingCart.getInstance(). AddItem (новый элемент (id));' – Budius

+0

@LucaNicoletti Но для того, чтобы установить слушателя, мне нужно иметь экземпляр самого низкого уровня на самом высоком уровне, чтобы установить его. Я сохранил указанные экземпляры на среднем уровне. Как и моя активность, отслеживаются следы ViewPager, и это, в свою очередь, отслеживает фрагменты с адаптером. У Activity нет экземпляра адаптера для установки на него слушателя. Прошу прощения, если это звучит неправильно в соответствии с шаблоном слушателя. –

ответ

0

Для связи между компонентами (Activity, Fragment) вы необходимо использовать шину событий. В андроида, вы можете выбрать между:

blog объяснить это.

+0

@NarayanAcharya Полезно ли это? – LaurentY

1

мои комментарии были:

что вы должны/могли бы сделать это, чтобы иметь глобальный репозиторий данных, который держит корзину и слушатель, связанную с изменениями к нему. Подобно singleton, например ShoppingCart.getInstance(). AddListener (this); и ShoppingCart.getInstance().addItem (новый элемент (id));

и

Да. Вот что я предлагаю. Не забывайте, что этот Синглтон никогда не сможет использовать Контекст или Активность, потому что вы не хотите утечки памяти, поэтому всегда вызывайте removeListener. По моему мнению, это уменьшит зависимость, так как все ваши контроллеры будут взаимодействовать только с моделью данных

и я добавлю код, который будет служить примером в качестве правильного ответа.

Ниже приведено очень грубое, введенное по коду сердца, но оно должно дать представление. Все элементы пользовательского интерфейса привязаны только к данным, а не друг к другу.

Аналогичные вещи могут быть реализованы с библиотеками, которые предоставляют шаблон observable для объектов только для данных.

public class ShoppingCart { 

    private ShoppingCart single; 
    private static void init(){ 
     .. init single if not null 
    } 

    private List<Item> items = new ArrayList<>(); 
    public int numberOfItems; 
    public long totalPrice; 

    private static void addItem(Item item){ 
     init() 
     single.items.add(item); 
     single.numberOfItems++; 
     single.totalPrice+=item.price; 
     dispatchChange(); 
    } 

    private static void removeItem(Item item){ 
     init(); 
     single.numberOfItems--; 
     single.totalPrice-=item.price; 
     dispatchChange(); 
     single.items.remove(item); 
    } 

    private void dispatchChange(){ 
     // TODO: write real loop here 
     for(single.listeners) listener.onCartChanged(single.cart); 
    } 
    public interface Listener { 
     void onCartChanged(ShoppingCart cart); 
    } 
    private List<Listener> listeners = new ArrayList<>(); 
    // TODO: addListener and removeListener code 

    public static class Item { 
    String id; 
    String name; 
    long price; 
    } 

} 
Смежные вопросы