【问题标题】:Communicating between components in AndroidAndroid中组件之间的通信
【发布时间】:2016-06-30 14:38:28
【问题描述】:

所以我有一个活动。 Activity 托管一个带有选项卡的 ViewPager,每个选项卡都包含一个 Fragment。 Fragments 本身每个都有一个 RecyclerView。我需要将 RecyclerView 的适配器的更改传达给活动。

目前,我正在使用侦听器模式并使用每个组件之间的接口进行通信。即我在 RecyclerView 的适配器和持有它的 Fragment 之间有一个接口。然后是从 Fragment 到创建所有 Fragment 的 ViewPager 的 FragmentStatePagerAdapter 的接口。 ViewPager 的适配器和承载 ViewPager 的 Activity 之间还有 1 个接口。我觉得所有组件的接口太多了,因为它们的结构。

目前我没有遇到这样的问题,但我认为由于所有嵌套组件,侦听器模式就像一个反模式。与其创建独立的组件,我认为层次结构将使将来难以进行代码更改。

我做得正确还是有更好的方法?这是我应该使用事件总线或观察者模式的情况吗(如果是,你能指出一些例子,有人使用它克服了类似的问题)吗?

注意:如果重要,我需要它来维护 Activity 中的全局对象,例如购物车,我可以在其中添加或删除项目,这些项目存在于 RecyclerView 的适配器中,我可以将其添加到购物车并且还增加或减少特定项目的计数。 ViewPager 和 Tabs 有助于将这些项目分成不同的类别。

编辑 1:一些代码尝试了@LucaNicoletti 的方法 - 我跳过了一个级别,即 ViewPager 的 FragmentStatePagerAdapter 级别。我想这应该没关系,并剥离了一些其他代码以保持它的小。

主活动:

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;
            }
        }
}

【问题讨论】:

  • 如果中间的监听器只是将动作转发到更高级别,则将接口移除到该级别,并使用更高的接口
  • 您应该/可以做的是拥有一个全局数据存储库,其中包含与更改相关联的购物车和侦听器。像一个单例,像ShoppingCart.getInstance().addListener(this);ShoppingCart.getInstance().addItem(new Item(id));
  • @LucaNicoletti 但是为了设置监听器,我需要在最高级别有一个最低级别的实例来设置它。我将上述实例保持在中等水平。就像我的 Activity 跟踪 ViewPager 并反过来跟踪具有适配器的 Fragments。 Activity 没有要在其上设置侦听器的 Adapter 实例。如果根据听众模式,这听起来有误,我深表歉意。
  • 在最底层声明一个接口:LowestLevelDelegate,将其标记为public,并在你的activity中实现这个接口。您的活动将实现此接口内的方法,但最低级别的片段/适配器将通过具有接口的实例来调用接口方法
  • @Budius 你是在建议我维持一个单身人士以避免听众大惊小怪吗? (奇怪我怎么没想到!:|)我需要再考虑一下,我想这会增加使用 ShoppingCart 实例的每个组件与 ShoppingCart 本身之间的依赖关系。如果我错了,请纠正我。

标签: android android-fragments design-patterns interface listener


【解决方案1】:

我的 cmets 是:

您应该/可以做的是拥有一个全局数据存储库,其中包含与更改相关联的购物车和侦听器。像单例,像 ShoppingCart.getInstance().addListener(this);和 ShoppingCart.getInstance().addItem(new Item(id));

是的。这就是我的建议。不要忘记这个 Singleton 永远不能保存 Context 或 Activity,因为你不想泄漏内存,所以总是调用 removeListener。在我看来,它会减少依赖,因为你所有的视图控制器只与数据模型交互

我将添加一些代码作为正确答案的示例。

下面是一个非常粗略的,由心脏代码键入的,但它应该给出一个想法。所有 UI 元素都只与数据相关联,而不是彼此相关联。

类似的东西可以通过为纯数据对象提供开箱即用的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;
  }

}

【讨论】:

    【解决方案2】:

    要在组件之间进行通信(ActivityFragment),您必须使用事件总线。 在 android 中,您可以选择:

    blog 解释这一点。

    【讨论】:

    • @NarayanAcharya 有用吗?
    猜你喜欢
    • 2014-12-21
    • 2017-01-28
    • 1970-01-01
    • 1970-01-01
    • 2019-05-30
    • 2016-06-09
    • 2015-09-18
    • 2016-05-01
    • 2017-09-04
    相关资源
    最近更新 更多