Android - how to find multiple views with common attribute

后端 未结 8 1885
野趣味
野趣味 2020-12-05 02:28

I have a layout with multiple ImageViews, some of those images need to have the same onClickListener. I would like my code to be flexible and to be able to get

相关标签:
8条回答
  • 2020-12-05 02:47
    @Override
    protected void onClick(View view){
    switch(view.getId()){
    
    case R.id.whatEverImageViewId :
    //....
    break ;
    
    case R.id.whatEverImageViewId 2 :
    //....
    break ;
    ....
    

    and you can use for loop to add listeners

    0 讨论(0)
  • 2020-12-05 02:48

    This method provides a general way of obtaining views that match a given criteria. To use simply implement a ViewMatcher interface

    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  
        }
    
    }
    
    0 讨论(0)
  • 2020-12-05 02:49

    Shlomi Schwartz's method has one flaw, it does not collect Views wchich are ViewGroups. Here is my fix:

    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;
    }
    
    0 讨论(0)
  • 2020-12-05 02:50

    I will share my functional-style method with a filter, can be used with StreamSupport library.

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

    Usage example:

    Optional.ofNullable(navigationViewWithSubViews)
                .map(subViews(hasType(NavigationView.class)))
                .orElse(RefStreams.empty())
                .forEach(__ -> __.setNavigationItemSelectedListener(this));
    
    0 讨论(0)
  • 2020-12-05 02:50

    This is a modification of Shlomi Schwartz's answer above. All credit to them. I didn't like how the recursion looked, and needed to be able to regex match a string tag name.

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

    For example:

    Collection<View> itemNameViews = findViewsByTag(v, "^item_name_[0-9]+$");
    
    0 讨论(0)
  • 2020-12-05 02:56

    You can use switch() for multiple widgets.

    switch(viewobject.getId()) {    
     case R.id.imageview1:    
       /* ... */    
       break;    
     case R.id.imageview2:    
       /* ... */      
       break;    
    }
    
    0 讨论(0)
提交回复
热议问题