I have a layout with multiple ImageView
s, some of those images need to have the same onClickListener.
I would like my code to be flexible and to be able to get
@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
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
}
}
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;
}
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));
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]+$");
You can use switch()
for multiple widgets.
switch(viewobject.getId()) {
case R.id.imageview1:
/* ... */
break;
case R.id.imageview2:
/* ... */
break;
}