Android - 任何Activity的根视图层次结构中所有视图的相对路径生成器

11

背景

在 Android 布局文件中,有很多 UI 元素和视图组。有时我们不需要给视图提供 ID 值(唯一标识符)。在这种情况下,我们无法通过 findViewByid() 找到视图,因此也就不能对它们进行操作。

问题是

如何为任何活动的所有视图生成路径,示例如下:

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

上述行的含义是

  1. Content 是主要布局
  2. LinearLayout 是顶层布局
  3. RelativeLayout-3 是顶级布局的第三个子元素
  4. LinearLayout 是第三个 RelativeLayout 的子元素
  5. TextView-2 是子元素 LinearLayout 的子元素,该 LinearLayout 是顶级 LinearLayout 的第三个 RelativeLayout 子元素。

因此,我要找的函数基本上是以下形式:

String path = getViewPath(view);

View view = findViewByPath(path)

使用案例:

服务器会通过提及视图路径向移动应用程序广播一些命令,

然后移动应用程序将从路径中查找视图并更改视图的属性。


这个问题从标题上来看不是很清楚。您想获取任何“View”对象的视图层次结构吗? - SMR
不,我想获取视图层次结构中所有视图的相对路径,例如布局顶部布局内视图的深度。 - Lavekush Agrawal
检查一下我的答案Lavekush!有另一种路径的替代方案。只需要一个标签即可! - Xenolion
4个回答

5
以下是上述问题的解决方案,我创建了两种方法来获取视图路径和通过路径获取视图。
祝贺!!!
    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;
  }
}

0

没有必要重复造轮子。如果您需要引用您的视图,有很多方法可以获得而不是通过路径来获取视图。如果您想从视图中获取引用,请按照以下步骤操作:

从Xml文件中:

如果您的视图是使用XML创建的,则最好的引用这些View的方法是设置一个ID,例如:

    android:id="@+id/my_id"

当你想在代码中引用那个 View 时,我们使用:

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

从代码(动态):

如果您的视图是动态创建的,那么我们无法使用方法(Parent).findViewById(int id)

或者

当您动态创建视图时,我们可以使用我们所谓的标签来引用它们。与ID不同,标签是在代码中动态设置到您的视图中的。标签可以是任何Java Object,但我们通常使用字符串,但您可以将标签与任何Object关联,并稍后使用方法findViewWithTag(Object tag)引用该视图

例子:

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

当我们想在代码中引用我们的TextView时,我们会调用该方法:

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

这样我们就可以得到对我们的View的引用,所以如果你想要TextView,你可以这样做。甚至View类还支持一种在代码中获取视图标记的方法,例如:

    String myTag=myTextView.getTag();

对于您的使用情况,最佳方法是:

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

要获取更多关于使用这些方法以及其他方法的文档和详细说明,请查看此处的文档。


这在我的情况下不起作用,我们没有创建任何动态视图,视图操作是从服务器完成的,服务器将向移动设备发送一些视图属性,然后移动设备从路径中找到视图并更改属性。 - Lavekush Agrawal
为什么你的服务器想要一个路径而不是只是一个“标签”来改变视图的属性?给我一个理由? - Xenolion
不用担心,我已经实现了解决方案。请检查我的答案。干杯 - Lavekush Agrawal
嗯,假设我已经理解了! - Xenolion

0

我认为最简单的解决方法是使用ViewGroupgetChildAt()getChildCount()方法。

因此,您可以将每个所需的View强制转换为ViewGroup,并调用上述方法。虽然这不会返回String路径,但您仍然可以获得相对层次结构,并逐步解决您特定的用例。


这个答案无法满足我的需求,因为我不知道运行时子视图的位置。实际上,服务器会通过指定视图路径向移动应用程序发送一些命令,然后移动应用程序将查找该视图并更改其属性。即使从计数或数字中也无法预测它是哪种类型的布局。 - Lavekush Agrawal
我会再仔细思考一下并回复你。希望你能尽快解决问题! - Rohan Stark

0
也许我过于简化了,但听起来你想要的是DOM。使用它,您将能够查找所有子项及其路径并查询结构。
如果确实回答了您的要求,那么您可以使用this或像不同答案中建议的那样,使用ViewGroup函数从根视图构建自己的DOM对象(您将使用get rootget children)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接