19

I'm trying to identify the parent view of an ui element so I can navigate through the UI freely.

For example, in Settings app, I can find the view with the text "Bluetooth":

UiObject btView = new UiObject(new UiSelector().text("Bluetooth"));

Now, the part where I get stuck is this one: I want to navigate two levels up and start a new search for the on/off button that enables and disables bluetooth.

Note: I can get the button if I use the code below.

UiObject btButtonView = new UiObject(new UiSelector().className("android.widget.Switch").instance(1));

This searches for switch buttons and returns the second encounter. I want the search to be more precise and look for the button in the linear layout that contains the "Bluetooth" text.

UPDATE: This is the layout of the Settings app (the Bluetooth part that I need):

LinearLayout
    LinearLayout
        ImageView
    RelativeLayout
        TextView (with text = "Bluetooth")
    Switch ()
Xiao
  • 12,235
  • 2
  • 29
  • 36
Gabriel Porumb
  • 1,661
  • 1
  • 12
  • 21

6 Answers6

17

You need to find the UiObject two levels up first using the text. This can be done using the getChildByText() methods in UiCollection or UiScrollable. Then you can easily find the switch. For 'Settings' this code works on my device:

UiScrollable settingsList = new UiScrollable(new UiSelector().scrollable(true));
UiObject btItem = settingsList.getChildByText(new UiSelector().className(LinearLayout.class.getName()),"Bluetooth", true);

UiObject btSwitch = btItem.getChild(new UiSelector().className(android.widget.Switch.class.getName()));
btSwitch.click();
Anders
  • 419
  • 4
  • 7
  • 1
    This method does not work either. The UiObject two levels up is a LinearLayout, but it does not have the text attribute. So I can't identify it by the text "Bluetooth". Updated the question with the actual layout of the app. – Gabriel Porumb Jul 22 '13 at 11:19
  • 1
    It does work. You don't identify by the text of the LinearLayout. "It looks for any child matching the childPattern argument that has a child UI element anywhere within its sub hierarchy that has a text attribute equal to text." In other words, you look for a LinearLayout that has a child with the text Bluetooth. – Anders Jul 23 '13 at 13:57
  • Yes, it works. It seems I did something wrong the first time. – Gabriel Porumb Jul 23 '13 at 14:34
  • Why is the parent element identified as scrollable? I thought it was a LinearLayout element – Ishmael7 May 23 '22 at 10:06
4

Below code works for me.

//Getting the scrollable view

UiScrollable settingsList = new UiScrollable(new UiSelector().scrollable(true));

for (int i=0; i<=settingsList.getChildCount(new UiSelector ().className(LinearLayout.class.getName())); i++) {
//Looping through each linear layout view
UiObject linearLayout = settingsList.getChild(new UiSelector().className(LinearLayout.class.getName()).instance(i));

//Checking if linear layout have the text. If yes, get the switch, click and break out of the loop.
if (linearLayout.getChild(new UiSelector ().text("Bluetooth")).exists()) {
    UiObject btSwitch = linearLayout.getChild(new UiSelector().className(android.widget.Switch.class.getName()));
    btSwitch.click ();
    break;
    }
}
0

If you want to just search for ON/OFF slider -> You can directly search for bluetooth OFF/ON button and click on it to disable/enable bluetooth -

You can check screenshot of the bluetooth page(using command - uiautomatorviewer) in command prompt and see that OFF button will have text in the OFF/ON slider. Then simply use -

   new UiObject(new UiSelector().text("OFF")).click();
Smriti
  • 1,552
  • 3
  • 15
  • 29
  • 1
    I can identify the button using its class name. I just select the second instance of that ui object. I wanted to search for a ui element in the parent view of an element which can be identified 100%, no matter what my layout is at one point. So, the question still is: how can I identify the parent of an ui elemen? – Gabriel Porumb Jul 17 '13 at 10:38
0

recently found that we could use getFromParent (for UiObject) and fromParent (for UiSelector) to select an uncle of object for example. If we have such layout:

`LinearLayout
    relative layout
        text View
    relative layout
        check box`

we could get checkbox from textview with this code:

TextViewTitle().getFromParent(new UiSelector()
            .fromParent(new UiSelector()
                    .resourceId("android:id/checkbox")));

where TextViewTitle is a Uiobject with text view

13bit
  • 1
0

uiautomator not support: get parent node directly

But you can add it by yourself (which need many work)

General steps:

  1. add xpath for uiautomator
  2. using xpath to locate parent node d.xpath("/current_node_path/..")

Additional Node:

I use

to successfully find parent node by:

self.driver.xpath("//android.widget.TextView[@text='Contact']/..")

and full code:

    def isMatchNode(self, curNodeAttrib, toMathInfo):
        isAllMatch = True
        for eachKey, eachToMatchValue in toMathInfo.items():
            if eachKey not in curNodeAttrib:
                isAllMatch = False
                break

            curValue = curNodeAttrib[eachKey]
            if curValue != eachToMatchValue:
                isAllMatch = False
                break


        return isAllMatch


    def findParentNode(self, curNodeXpath, matchDict, maxUpLevel=3):
        matchNode = None

        curNode = self.driver.xpath(curNodeXpath).get()
        curNodeAttrib = curNode.attrib # .attrib contain 'clickable'
        # curNodeInfo = curNode.info # .info not contain 'clickable'
        isCurMatch = self.isMatchNode(curNodeAttrib, matchDict)
        if isCurMatch:
            # current is match
            matchNode = curNode
        else:
            # try parent nodes
            curUpLevel = 1
            curParentNodeXpath = curNodeXpath
            hasFound = False
            while((not hasFound) and (curUpLevel <= maxUpLevel)):
                curParentNodeXpath += "/.."
                curParentNode = self.driver.xpath(curParentNodeXpath).get()
                curParentNodeAttrib = curParentNode.attrib
                isCurParentMatch = self.isMatchNode(curParentNodeAttrib, matchDict)
                if isCurParentMatch:
                    matchNode = curParentNode
                    break


        return matchNode


    def location_WexinAdd(self, reload=False):
        for eachNodeText in ["Contact", "Public Account"]:
            eachNodeXpath = "//android.widget.TextView[@text='%s']" % eachNodeText
            matchDict = {"clickable": "true"}
            clickableParentNode = self.findParentNode(curNodeXpath=eachNodeXpath, matchDict=matchDict)
            if clickableParentNode:
                clickableParentNode.click()
            else:
                logging.warning("Fail click %s for not found clickable=true (parent) node", eachNodeText)

for your refer.

crifan
  • 12,947
  • 1
  • 71
  • 56
0

For this, you should use UiObject2. It's much easier to get the ascendant hierarchy with it than with UiObject.

Here's a solution with UiObject2:

val labelText: UiObject2 = device.wait(Until.findObject(By.text("Bluetooth")
            .clazz("android.widget.TextView")), 2000)

val checkboxView = labelText.parent.parent.findObject(By.clazz("android.widget.Switch"))
broand
  • 1
  • 3