Home » Android » Android – Relative path generator of all view present inside rootview hierarchy of any Activity

Android – Relative path generator of all view present inside rootview hierarchy of any Activity

Posted by: admin May 14, 2020 Leave a comment

Questions:

Background

In android layout file there or lot of UI elements and view group. Sometimes we do not need to give id value ( unique identifier ) to views. In such scenarios we can’t find the view by saying findViewByid(). hence we cannot manipulate them.

Question is

How we can generate a path for all view of any activity, Example is below:

content>LinearLayout-0>RelativeLayout-3>LinearLayout-0>TextView-2

The meaning of above line is

  1. Content is main layout
  2. LinearLayoutis top most layout
  3. RelativeLayout-3 is 3rd child of top most layout
  4. LinearLayout is child of 3rd RelativeLayout
  5. TexView-2 is child of LinearLayout which is 3rd RelativeLayout child of topmost LinearLayout.

So basically I’m looking for function like below:

String path = getViewPath(view);

and

View view = findViewByPath(path)

Use case:

Actually server will broadcast some command to mobile app by menting path of view ,

then mobile app will find the view from the path and change the property of the views

How to&Answers:

Below the solution for above question, I have created both method for getting view path, and getting view by path.

Cheers!!!

    package com.test;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;

import java.util.Arrays;


public class CustomViewIdManager {


  private static final String ACTIVITY_CLASS_SEPARATOR = "@";
  private static final String VIEW_SEPARATOR = ">";
  private static final String VIEW_POSITION_SEPARATOR = ":";
  private static final String MAIN_CONTENT_LAYOUT_NAME = "content";


  /**
   * Find given view path in activity hierarchy
   *
   * @param view
   * @param activity
   * @return Path given view
   */
  public static String generateViewPathInActivityViewHierarchy(View view, Activity activity) {

    String path = "";
    View currentView = view;
    ViewParent currentParent;

    do {
      currentParent = currentView.getParent();
      if (currentView.getId() == Window.ID_ANDROID_CONTENT) {
        path = activity.getLocalClassName() + ACTIVITY_CLASS_SEPARATOR + MAIN_CONTENT_LAYOUT_NAME + path;
        break;
      } else {
        path = VIEW_SEPARATOR + currentView.getClass().getSimpleName() + VIEW_POSITION_SEPARATOR + getSelfIndexInParent((View) currentParent, currentView) + path;
      }
      currentView = (View) currentView.getParent();
    } while (true);

    return path;
  }


  /**
   * Finding the view by given path in activity view hierarchy
   * @param path
   * @param activity
   * @return
     */
  public static View findViewByCustomPath(String path, Activity activity) {

    String[] activitySplitting = path.split(ACTIVITY_CLASS_SEPARATOR);
    String[] viewSplitting = activitySplitting[1].split(VIEW_SEPARATOR);

    View viewLooker = null;

    if (viewSplitting[0].equalsIgnoreCase(MAIN_CONTENT_LAYOUT_NAME)) {
      viewLooker = ViewUtil.getContentView(activity);
    }

    return viewFinder(viewLooker, Arrays.copyOfRange(viewSplitting, 1, viewSplitting.length));

  }

  public static View viewFinder(View view, String[] restPath) {

    View viewToSendBack;

    String singleView = restPath[0];
    String[] viewPositioningSplitting = singleView.split(VIEW_POSITION_SEPARATOR);
    viewToSendBack = ((ViewGroup) view).getChildAt(Integer.parseInt(viewPositioningSplitting[1]));

    if (restPath.length > 1) {
      return viewFinder(viewToSendBack, Arrays.copyOfRange(restPath, 1, restPath.length));
    } else {
      return viewToSendBack;
    }
  }


  /**
   * This will calculate the self position inside view
   *
   * @param parent
   * @param view
   * @return index of child
   */
  public static int getSelfIndexInParent(View parent, View view) {

    int index = -1;
    if (parent instanceof ViewGroup) {
      ViewGroup viewParent = (ViewGroup) parent;

      for (int i = 0; i < viewParent.getChildCount(); ++i) {
        View child = viewParent.getChildAt(i);
        ++index;

        if (child == view) {
          return index;
        }
      }
    }

    return index;
  }
}

Answer:

I think the simplest solution to this would be to use a ViewGroup‘s getChildAt() and getChildCount() methods.

So, you can cast each of your desired View as a ViewGroup and can call the above methods. Though, this won’t return a String path. But you could still obtain a relative hierarchy and work your way up to your specific use case.

Answer:

There is no need to reinvent the wheel. If you need the reference to your Views there is a lot of ways to get than making path to views. If you want the reference from Views here is what to do:

From Xml:

If your views were made using XML then the best way to refer to those View is by setting an id like:

    android:id="@+id/my_id"

And when you want to refer to that View in code we use:

    (AnyParentOfTheView).findViewById(R.id.my_id);

From Code (Dynamically):

If your Views were created dynamically then we cannot use the method (Parent).findViewById(int id).

Alternatively

When you create Views dynamically we can refer to them using what we call Tags. Unlike ids, Tags are set in code to your views dynamically. A tag can be any Java Object but we usually use Strings but you can associate a tag with any Object and later refer to the view using the method findViewWithTag(Object tag)

Example:

    .......
    TextView myTextView=new TextView(this);
    myTextView.setTag("txt");
    .......

And when we want to refer to our TextView in code we call the method like:

    TextView myTextView=(TextView)findViewWithTag("txt");

And like that we get reference to our View so if you want the TextView you can do that. The View class even support a way to get a view tag in code like:

    String myTag=myTextView.getTag();

Best methods for your use case are:

    setTag(Object obj)
    setTag (int key, Object tag)
    getTag(int key)
    getTag()
    findViewWithTag()

To get more documentation and elaboration on using these methods and more. Check the documentation
HERE

Answer:

Maybe I am over simplifying but it sounds like what you want is the DOM.
Using the it you would be able to find all children and their paths and query the structure.

If indeed this answers your requirement then you can use this or like suggested in a different answer by using ViewGroup functions building your own DOM object from the root view (you will use get root and get children.