Cannot compute child count in ListView, RecyclerView or any other scrollable view in Android with Appium

Is there any built in support in appium to get child count of a recycler view, where all the children are not visible on screen.

I mean some may be visible and others may become visible as you scroll down the page.

Although I have developed a workaround but with the amount of time the code takes to execute and its associated inaccuracies make it a big pain.

There is no easy way I know of. What’s your particular use case that requires you to count your ListView’s children?

There is a page in my application which shows a number of offers to a user.

Now all these offers come as a response of an API call, so there can be any no. of offers.

And when I run my script I make that API call explicitly through my java code and get the offer count. Now the use case is to cross verify that an equal no of offers are shown on UI as children of this ListView.

Please find below pseudo code which may help you:
//considering these are your expected list values
String[] arrExpectedListVals={“Abbaye de Belloc”,“Abbaye du Mont des Cats”,“Abertam”};
System.out.println(arrExpectedListVals.length);

//Consider this will return all your textview or list of item values
List listEles=driver.findElementsByXPath("//android.widget.ListView[@resource-id=‘android:id/list’]/android.widget.TextView");
System.out.println(listEles.size());

		for(int i=0;i<arrExpectedListVals.length;i++)
		{
			String strSearchValue=arrExpectedListVals[i];
			boolean found=false;
			for(int j=0;j<listEles.size();j++)
			{
				String strListValue=listEles.get(j).getText();
				if(strSearchValue.equalsIgnoreCase(strListValue))
				{
					found=true;
					break;
				}
			}
			if(found)
				System.out.println("List value '"+strSearchValue+"' exists");
			else
				System.out.println("List value '"+strSearchValue+"' does not exists");
		}

@UD I see the following issues with the solution that you provided

  1. listEles will only provide the elements which are currently visible on screen and not the ones which will become visible upon scrolling down

  2. This solution will only tell me if a particular text appears in a child element and cannot catch a bug if a particular text is displayed twice as a list view child

  3. The solution cannot tell me the count of the children of the entire list (visible + non visible)

  4. The solution will not work if I have many different types of children like first is LinearLayout second may be is a RelativeLayout and then third may be a FrameLayout and so on

  5. This solution cannot give me an element at a particular index in ListView

  6. In case the child elements are images this will again not work (I mean if the child happens to be an image i would get a URL in the api response and this is the URL where the image is hosted, so if there could be a way in which i can compare the bytes of this image against the response bytes from that URL that would be great)

1 Like

@jlipps @isaac @sebv @jonahss This is a very commonly encountered problem

And is also one of the biggest drawbacks of Appium (as of now) against other tools like Robotium and Espresso, as far I have seen for the automation of Android Apps.

Also, given that this can be done easily in UIAutomator (with UiCollection & UiScrollable) makes me believe that it should not be a big task for you guys to implement

Please have this looked into by some one from your team ASAP

I agree. This is a pretty major pain when writing tests with Appium.

I can understand some of the difficulties with implementing some feature to count the number of items in a list view, however. Robotium and Espresso (white box, instrumented tests in general) have the advantage of being white box tests, so they can query the adapter backing the list view directly for the information of the child elements (and they can also control the list view to scroll to exact positions). OTOH, UiAutomator was intended for black box tests, since the benefit of UiAutomator was to switch between various different apps in different processes. Unfortunately, this also meant that UiAutomater loses the ability to query adapters and list views for the number of data items.

If I know my UiAutomator well, UiCollection and UiScrollable will scroll through the list and search the child elements that match a particular UiSelector and not necessarily count all the child elements of the list.

1 Like

Ask your Android dev’s to set content description as value(index + some string) in there get view method. Now if you scroll also that element will contain adapter index in the content description field.

This will give unique element.

Now If Appium provides a method to scrolltoelement like it provided scrolltoexact then it would be a great help.

Please tag Appium owners or community developers.

@Dhiren_Mudgil @afwang

I think I have found a solution, that probably doesn’t need to make changes to my app source code and also it does not require me to go through a complex set of operations.

I can just add a new handler under io.appium.handler and register it in AndroidCommandExecutor compile it into a class file and subsequently into a dex and then place it in android bootstrap and then I think we can perform the action with the name I registered it.

Let me check it out…will let u know if it work…

@jlipps @isaac @sebv @jonahss

Hi Team,

Can we have these two methods provided by UIScrollable class with appium client

scrollIntoView(UiSelector selector)
Perform a scroll forward action to move through the scrollable layout element until a visible item that matches the selector is found.

scrollDescriptionIntoView(String text)
Performs a forward scroll action on the scrollable layout element until the content-description is found, or until swipe attempts have been exhausted.

@khageshkapilSD

For the time being you can use below code

String contentDescription = “Ad List Container 8”;

String uiSelector = "new UiSelector().descriptionContains(\"" + contentDescription + "\")";

((AndroidDriver) MobileAndriodTestDriverImpl.driver).findElementByAndroidUIAutomator("new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView("
        + uiSelector + ".instance(0));");

It will automatically scroll to the element which contains the given context description. All you need to ask your developer to append it. :slight_smile:

1 Like

Hey, if you got something interesting, definitely post your code and leave it up somewhere. I’m sure everyone would be glad to check it out.

@Dhiren_Mudgil,

by looking at the scrollTo method, i see the same code that you provided above.

As per the scrollTo method, if the element contains either description or text, it will try to scroll and find the element.
What is the difference between your code and using scrollTo method?

@UD Ini tried using scrollTo but i didn’t worked for me. I think it works on both content and text.

String uiScrollables =
UiScrollable(“new UiSelector().descriptionContains(”" + text + “”)") + UiScrollable(
“new UiSelector().textContains(”" + text + “”)");
return findElementByAndroidUIAutomator(uiScrollables);

In my solution it only work on content description as in my case i need to verify the text from a web request.

Hello all - I see that the last post in this thread was April '16. I’m encountering the exact problem described here, and I was wondering if there is now any best practice or accepted convention for handling off-screen list items.

I’m trying to iterate through a list of titles and tap the one that I’m passing as a parameter, but the only items being recognized by Appium are the ones that are visible on the screen. I totally understand why this is happening - just wondering if, after a year and a half, there is a reliable, widely used method of dealing with this issue.

Thanks!

We are doing functional tests, so basically mimic what a person does using the app.

If we need to tap some tittle on a list that is not visible, how does a normal user does it?
If we need to count the elements that are present on a list, how does a normal user does it?

Just code those user actions.

I don’t want a function that returns a count of elements present, much less a value in content-description. Why should I trust that value? Its probably set by the same condition that may be causing a bug and we would not detect that bug.

I didn’t want a function that returned the count of elements either. I wanted to tap on a certain item in a list of unique titles if it is present.

I figured it out. I created a Set and added all of the visible titles in the list to that Set. Then I scrolled down and retrieved the next group of titles visible on the screen. If a title was not in the set, I added it and incremented a counter by 1. I repeat this scrolling and recording process until the counter equals 0 (or until the item that I want to tap is tapped), and then I know I’ve gone through all items in the list. If the items is found, it can be tapped without having to go through the whole list. If the counter equals zero, I’ve gone through the whole list without finding what I was looking for. So far, it’s working as expected for pass and fail scenarios.

Note: This probably isn’t the best solution for dealing with really long lists, but it works well within the scope of my app. Maximum “down swipes” might be 4 or 5, but it’s usually no more than two.

1 Like

and how do you “scroll down” ? by using touch actions ?

Yes, I use TouchActions:

public static void scrollDown(AndroidDriver<MobileElement> driver, int numSwipes) {
		
		int counter = 0;
		
		while (counter < numSwipes) {
			
			TouchAction actionScrollDown = new TouchAction(driver);
			actionScrollDown = new TouchAction(driver).press(getScreenWidth(driver)/2, (int) (getScreenHeight(driver)*.8)).moveTo(0, (int) (-getScreenHeight(driver)*.8)).release();
			actionScrollDown.perform();
			
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			counter++;
		}	
	}

We’ve tried that as well , the problem is if the list contains too many items, it takes ages to find the item on the bottom of the list