【问题标题】:Android - how to find multiple views with common attributeAndroid - 如何找到具有共同属性的多个视图
【发布时间】:2012-02-07 16:43:51
【问题描述】:

我有一个包含多个 ImageViews 的布局,其中一些图像需要具有相同的 onClickListener。 我希望我的代码具有灵活性,并且能够获取这些 Views 的集合,在运行时迭代并添加侦听器。

是否有像findViewById 这样的方法可以返回Views 的集合,而不仅仅是一个?

【问题讨论】:

标签: android view


【解决方案1】:

在 kotlin 中我们可以这样做是一种非常简单的方式,如下所示(Kotlin 扩展功能):

fun ViewGroup.findViewsByTag(tag: String): ArrayList<View> = ArrayList<View>().also {
    this.children.forEach { child ->
        if (child is ViewGroup) it.addAll(child.findViewsByTag(tag))
        if (child.tag == tag) it.add(child)
    }
}

现在,您可以在任何 viewGroup 上使用它。例如:

mViewGroup.findViewsByTag("my tag")

【讨论】:

    【解决方案2】:

    我终于写出了这个方法(更新感谢@SuitUp(更正了用户名)):

    private static ArrayList<View> getViewsByTag(ViewGroup root, String tag){
        ArrayList<View> views = new ArrayList<View>();
        final int childCount = root.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = root.getChildAt(i);
            if (child instanceof ViewGroup) {
                views.addAll(getViewsByTag((ViewGroup) child, tag));
            } 
    
            final Object tagObj = child.getTag();
            if (tagObj != null && tagObj.equals(tag)) {
                views.add(child);
            }
    
        }
        return views;
    }
    

    它将返回所有具有android:tag="TAG_NAME" 属性的视图。享受;)

    【讨论】:

    • 它不收集 ViewGroup 视图。
    • 您可以使用 Objects.equal(tagObj, tag) 来获得更简洁的代码,并允许查询没有标签的视图。您还可以通过让标签参数成为对象来使该方法更通用,因为标签可能不是字符串。 (返回 List 或 Collection 会更好)。
    • 非常棒,+1。我创建了一个变体,如果它有标签,它也会返回根视图。 gist.github.com/orip/5566666
    • 为什么对每个变量都使用final?见stackoverflow.com/questions/4279420/…
    【解决方案3】:

    这是对 Shlomi Schwartz 上述答案的修改。所有功劳归功于他们。我不喜欢递归的样子,需要能够匹配字符串标签名称。

      /**
       * Find all views with (string) tags matching the given pattern.
       *
       */
      protected static Collection<View> findViewsByTag(View root, String tagPattern) {
        List<View> views = new ArrayList<>();
    
        final Object tagObj = root.getTag();
        if (tagObj instanceof String) {
          String tagString = (String) tagObj;
          if (tagString.matches(tagPattern)) {
            views.add(root);
          }
        }
    
        if (root instanceof ViewGroup) {
          ViewGroup vg = (ViewGroup) root;
          for (int i = 0; i < vg.getChildCount(); i++) {
            views.addAll(findViewsByTag(vg.getChildAt(i), tagPattern));
          }
        }
    
        return views;
      }
    

    例如:

    Collection<View> itemNameViews = findViewsByTag(v, "^item_name_[0-9]+$");
    

    【讨论】:

      【解决方案4】:

      我将与过滤器分享我的函数式方法,可以与 StreamSupport 库一起使用。

      @NonNull
      public static <T> Function<View, Stream<T>> subViews(
          @NonNull final Function<View, Optional<T>> filter
      ) {
          return view -> RefStreams.concat(
              // If current view comply target condition adds it to the result (ViewGroup too)
              filter.apply(view).map(RefStreams::of).orElse(RefStreams.empty()),
      
              // Process children if current view is a ViewGroup
              ofNullable(view).filter(__ -> __ instanceof ViewGroup).map(__ -> (ViewGroup) __)
                      .map(viewGroup -> IntStreams.range(0, viewGroup.getChildCount())
                              .mapToObj(viewGroup::getChildAt)
                              .flatMap(subViews(filter)))
                      .orElse(RefStreams.empty()));
      }
      
      @NonNull
      public static <T> Function<View, Optional<T>> hasType(@NonNull final Class<T> type) {
          return view -> Optional.ofNullable(view).filter(type::isInstance).map(type::cast);
      }
      
      @NonNull
      public static Function<View, Optional<View>> hasTag(@NonNull final String tag) {
          return view -> Optional.ofNullable(view).filter(v -> Objects.equals(v.getTag(), tag));
      }
      

      使用示例:

      Optional.ofNullable(navigationViewWithSubViews)
                  .map(subViews(hasType(NavigationView.class)))
                  .orElse(RefStreams.empty())
                  .forEach(__ -> __.setNavigationItemSelectedListener(this));
      

      【讨论】:

        【解决方案5】:

        此方法提供了一种获取符合给定条件的视图的通用方法。要使用只需实现一个ViewMatcher 接口

        private static List<View> getMatchingViews(ViewGroup root, ViewMatcher viewMatcher){
            List<View> views = new ArrayList<View>();
            final int childCount = root.getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = root.getChildAt(i);
                if (child instanceof ViewGroup) {
                    views.addAll(getMatchingViews((ViewGroup) child, viewMatcher));
                }
        
                if(viewMatcher.matches(child)){
                    views.add(child);
                }
            }
            return views;
        }
        
        public interface ViewMatcher{
            public boolean matches(View v);
        }
        
        // Example1: matches views with the given tag
        public class TagMatcher implements ViewMatcher {
        
            private String tag;
        
            public TagMatcher(String tag){
                this.tag = tag;
            }
        
            public boolean matches(View v){
                String tag = v.getTag();
                return this.tag.equals(tag);
            }
        
        }
        
        
        // Example2: matches views that have visibility GONE
        public class GoneMatcher implements ViewMatcher {
        
            public boolean matches(View v){
                v.getVisibility() == View.GONE  
            }
        
        }
        

        【讨论】:

          【解决方案6】:

          您可以将switch() 用于多个小部件。

          switch(viewobject.getId()) {    
           case R.id.imageview1:    
             /* ... */    
             break;    
           case R.id.imageview2:    
             /* ... */      
             break;    
          }
          

          【讨论】:

            【解决方案7】:

            Shlomi Schwartz 的方法有一个缺陷,它不收集 Views 而不是 ViewGroups。这是我的解决方法:

            private static ArrayList<View> getViewsByTag(ViewGroup root, String tag){
                ArrayList<View> views = new ArrayList<View>();
                final int childCount = root.getChildCount();
                for (int i = 0; i < childCount; i++) {
                    final View child = root.getChildAt(i);
                    if (child instanceof ViewGroup) {
                        views.addAll(getViewsByTag((ViewGroup) child, tag));
                    } 
            
                    final Object tagObj = child.getTag();
                    if (tagObj != null && tagObj.equals(tag)) {
                        views.add(child);
                    }
            
                }
                return views;
            }
            

            【讨论】:

              【解决方案8】:

              没有像 findViewById 这样的方法可以返回一组视图,但是您可以遍历视图并将它们设置为 onClickListener,如下所示:

              for (int i = 0;i < layout.getChildCount();i++) {
                View child = layout.getChildAt(i);
                //you can check for view tag or something to see if it satisfies your criteria
                //child.setOnClickListener...
              }
              

              统一更新: 对于递归视图层次结构(这是来自真实项目的示例,我们在其中递归地进行视图刷新,但是您可以对嵌套视图执行任何操作):

              private void recursiveViewRefresh(ViewGroup view) {
                  for (int i = 0;i < view.getChildCount();i++) {
                      View child = view.getChildAt(i);
                      try {
                          ViewGroup viewGroup = (ViewGroup) child;
                          recursiveViewRefresh(viewGroup);
                      } catch (ClassCastException e) {
                          //just ignore the exception - it is used as a check
                      }
                      singleViewRefresh(child);
                  }
              }
              

              【讨论】:

              • 我会同意的,虽然奇怪的是没有本地方法。
              • 如果child是另外一个布局呢?是递归吗?
              • 是的,您可以像这样递归地执行此操作:(我会将其添加到答案中)
              • 谢谢已经实现,一旦 Stack' 让我回答,我会发布我的代码
              • 查看我添加的方法,我认为这是一个“好有”的工具。再次感谢您的帮助;)
              【解决方案9】:
              @Override
              protected void onClick(View view){
              switch(view.getId()){
              
              case R.id.whatEverImageViewId :
              //....
              break ;
              
              case R.id.whatEverImageViewId 2 :
              //....
              break ;
              ....
              

              你可以使用for循环来添加监听器

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-05-25
                • 1970-01-01
                • 1970-01-01
                • 2017-08-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多