For a hybrid app , I need to scroll till the end of the page. How can I do ?
I am able to scroll to exact element by using driver.scrollTo(); and driver.ScrollToExact();
But I want to scroll the app from top to bottom.
Can anyone tell me please?
#Test
public void testScroll()throws Exception
{
for(int i=0;i<4;i++)
{
Thread.sleep(2000);
if (driver.findElement(By.name("end_item")).isDisplayed())
{
driver.findElement(By.name("end_item")).click();
break;
}
else
{
verticalScroll();
}
}
}
public void verticalScroll()
{
size=driver.manage().window().getSize();
int y_start=(int)(size.height*0.60);
int y_end=(int)(size.height*0.30);
int x=size.width/2;
driver.swipe(x,y_start,x,y_end,4000);
}
This will help you to swipe till end or till whatever position you want.
You can use coordinates to scroll to the end of the page with even locating the String available on the page. Use this:
TouchAction action = new TouchAction(driver).longPress(20,y).moveTo(20, 10).release();
action.perform();
I too had the same problem. What I did is, used the text which is at the bottom of that particular page ("text should be dynamic, it should be constant") then used the following code
String text="ABC";
driver.scrollTo(text);
After this code you can perform any action.. for example see the below code
String text="ABC";
driver.scrollTo(text);
driver.findElement(By.xpath("//*[#text='"+text+"']")).click();
public List<String> getAllStudyMaterialName(List<WebElement> elements) {
List<String> documentTitle = new ArrayList<>();
boolean isStudyMaterialRepeat = false;
while (true) {
List<WebElement> titles = elements;
for (WebElement title : titles) {
documentTitle.add(title.getText());
}
scrollUp(driver);
if (documentTitle.get(documentTitle.size() - 1).toString().equals(elements.get(2).getText())
&& documentTitle.get(documentTitle.size() - 2).toString().equals(elements.get(1).getText())
&& documentTitle.get(documentTitle.size() - 3).toString().equals(elements.get(0).getText())) {
isStudyMaterialRepeat = true;
}
if (isStudyMaterialRepeat) {
break;
}
}
return documentTitle;
}
After scrolling till bottom I am checking it's a same elements
This logic will scroll till the bottom of the page and if it found last 3 elements are same with stored element it will break the loop
Related
I want to achieve a animated scrolling menu options in ANDROID like the following functionality in iOS
https://www.youtube.com/watch?v=1NkuChdWA_I
I have try to do it by onScrollUp and onScrollDown listview, but unable to find the desire result.
private class ParallaxStikkyAnimator extends HeaderStikkyAnimator {
#Override
public AnimatorBuilder getAnimatorBuilder() {
mHeader_image = getHeader().findViewById(R.id.ll_boxeshorizntal);
return AnimatorBuilder.create().applyVerticalParallax(mHeader_image);
}
#Override
public void onScroll(final int scrolledY) {
super.onScroll(scrolledY);
listview.setOnDetectScrollListener(new OnDetectScrollListener() {
#Override
public void onUpScrolling() {
// ll_boxes.setAlpha(1.0f);
Log.e("onUpScrolling", "Up scrolledY==" + scrolledY);
// slideToTop(mHeader_image, scrolledY);
if (scrolledY == 0) {
/* ll_four_boxes.setVisibility(View.VISIBLE);
ll_boxeshorizntal.setVisibility(View.GONE);*/
}
}
#Override
public void onDownScrolling() {
Log.e("onDownScrolling", "scrolledY==" + scrolledY);
// slideToBottom(mHeader_image, scrolledY);
// slideToBottom(mHeader_image, scrolledY);
DownScrolling(scrolledY);
if (scrolledY < 300) {
/* ll_four_boxes.setVisibility(View.GONE);
ll_boxeshorizntal.setVisibility(View.VISIBLE);*/
}
}
});
}
}
I can use the two view for Gone and Visible according to the scroll but how to achieve animation like iOS.
Basically i have to convert a GridView with Two row and Two column to One Row and Four Column with animation.
You can check how much scrolling is done by using scrollView.getScrollY(). And on the basis of the value make the buttons visible or hide or change their width accordingly so that you can fit four buttons in a single row and when it is at top then display 2 buttons per row.
I have a GridView loaded with data, and some items in the grid are "group" items
that contain a different set of data. The GridView can scroll if there is
a lot of data.
On some devices the GridView has three columns and on others there are only two columns depending on the device screen width.
What I need to test is to scroll to a particular(with text) item in the grid, open it and then close it(back button=simpleClick). Then scroll to a second item with a particular text and open and then close it(back button=simpleClick).
I have tried several approaches with swipes, and then performing clicks
on them, but this approach fails as you need to know exactly how many
swipes/scrolls to perform before I can click to open the item group - device specific which is bad. Also performing too many scrolls than what is required to reach the bottom of the screen seems to slowdown the test !!quite!! a bit.
private void scrollDown() {
Log.d(TAG, "scrollDown: ");
onView(withId(R.id.main_grid))
.perform(swipeUp());
}
private void scrollUp() {
Log.d(TAG, "scrollUp: ");
onView(withId(R.id.main_grid))
.perform(swipeDown());
}
private void clickOn(final String text) {
onView(withText(new Matcher<String>() {
#Override
public boolean matches(Object item) {
if (item instanceof String) {
if (((String) item).contains(text))
return true;
else
return false;
}
return false;
}
#Override
public void describeMismatch(Object item, Description mismatchDescription) {
}
#Override
public void _dont_implement_Matcher___instead_extend_BaseMatcher_() {
}
#Override
public void describeTo(Description description) {
}
}))
.perform(click());
}
I have tried this as well, but focus gets set to each Item
but as it reaches the end of the screen, one needs to scroll manually
on the device - it does not scroll the screen.
for (int i = 0 ; i < 20; i++) {
onData(anything())
.inAdapterView(allOf(withId(R.id.main_grid), isDisplayed()))
.atPosition(i)
.check(matches(isDisplayed()));
}
I have also tried using the onData which I cannot get to succeed(compile/build),
as I am using a CursorLoader and a ResourceCursorAdapter.
Any Example to solve my test case would great.
Yes, you should use onData statement to avoid problems with scrolling in GridView. You can take a look at example here - http://droidtestlab.com/adapterView.html
But in your case you will have android.database.Cursor interface as a parameter to onData. Something like this (here you specify that you want to wait for data in GridView where first column is equal "VALUE" and then click at this record):
onData(allOf(is(new BoundedMatcher<Object, Cursor>(Cursor.class) {
#Override
public void describeTo(Description description) {
}
#Override
protected boolean matchesSafely(Cursor cursor) {
return cursor.getString(0).equals("VALUE");
}
}))).inAdapterView(withId(<ADAPTER_ID>)).perform(click());
I'm building an interface similar to the Google Hangouts chat interface. New messages are added to the bottom of the list. Scrolling up to the top of the list will trigger a load of previous message history. When the history comes in from the network, those messages are added to the top of the list and should not trigger any kind of scroll from the position the user had stopped when the load was triggered. In other words, a "loading indicator" is shown at the top of the list:
Which is then replaced in-situ with any loaded history.
I have all of this working... except one thing that I've had to resort to reflection to accomplish. There are plenty of questions and answers involving merely saving and restoring a scroll position when adding items to the adapter attached to a ListView. My problem is that when I do something like the following (simplified but should be self-explanatory):
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
Then what the user will see is a quick flash to the top of the ListView, then a quick flash back to the right location. The problem is fairly obvious and discovered by many people: setSelection() is unhappy until after notifyDataSetChanged() and a redraw of ListView. So we have to post() to the view to give it a chance to draw. But that looks terrible.
I've "fixed" it by using reflection. I hate it. At its core, what I want to accomplish is reset the first position of the ListView without going through the rigamarole of the draw cycle until after I've set the position. To do that, there's a helpful field of ListView: mFirstPosition. By gawd, that's exactly what I need to adjust! Unfortunately, it's package-private. Also unfortunately, there doesn't appear to be any way to set it programmatically or influence it in any way that doesn't involve an invalidate cycle... yielding the ugly behavior.
So, reflection with a fallback on failure:
try {
Field field = AdapterView.class.getDeclaredField("mFirstPosition");
field.setAccessible(true);
field.setInt(listView, positionToSave);
}
catch (Exception e) { // CATCH ALL THE EXCEPTIONS </meme>
e.printStackTrace();
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
}
}
Does it work? Yes. Is it hideous? Yes. Will it work in the future? Who knows? Is there a better way? That's my question.
How do I accomplish this without reflection?
An answer might be "write your own ListView that can handle this." I'll merely ask whether you've seen the code for ListView.
EDIT: Working solution with no reflection based on Luksprog's comment/answer.
Luksprog recommended an OnPreDrawListener(). Fascinating! I've messed with ViewTreeObservers before, but never one of these. After some messing around, the following type of thing appears to work quite perfectly.
public void addNewItems(List<Item> items) {
final int positionToSave = listView.getFirstVisiblePosition();
adapter.addAll(items);
listView.post(new Runnable() {
#Override
public void run() {
listView.setSelection(positionToSave);
}
});
listView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if(listView.getFirstVisiblePosition() == positionToSave) {
listView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
else {
return false;
}
}
});
}
Very cool.
As I said in my comment, a OnPreDrawlistener could be another option to solve the problem. The idea of using the listener is to skip showing the ListView between the two states(after adding the data and after setting the selection to the right position). In the OnPreDrawListener(set with listViewReference.getViewTreeObserver().addOnPreDrawListener(listener);) you'll check the current visible position of the ListView and test it against the position which the ListView should show. If those don't match then make the listener's method return false to skip the frame and set the selection on the ListView to the right position. Setting the proper selection will trigger the draw listener again, this time the positions will match, in which case you'd unregister the OnPreDrawlistener and return true.
I was breaking up my head until I found a solution similar to this.
Before adding a set of items you have to save top distance of the firstVisible item and after adding the items do setSelectionFromTop().
Here is the code:
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
// for (Item item : items){
mListAdapter.add(item);
}
// restore index and top position
mList.setSelectionFromTop(index, top);
It works without any jump for me with a list of about 500 items :)
I took this code from this SO post: Retaining position in ListView after calling notifyDataSetChanged
The code suggested by the question author works, but it's dangerous.
For instance, this condition:
listView.getFirstVisiblePosition() == positionToSave
may always be true if no items were changed.
I had some problems with this aproach in a situation where any number of elements were added both above and below the current element. So I came up with a sligtly improved version:
/* This listener will block any listView redraws utils unlock() is called */
private class ListViewPredrawListener implements OnPreDrawListener {
private View view;
private boolean locked;
private ListViewPredrawListener(View view) {
this.view = view;
}
public void lock() {
if (!locked) {
locked = true;
view.getViewTreeObserver().addOnPreDrawListener(this);
}
}
public void unlock() {
if (locked) {
locked = false;
view.getViewTreeObserver().removeOnPreDrawListener(this);
}
}
#Override
public boolean onPreDraw() {
return false;
}
}
/* Method inside our BaseAdapter */
private updateList(List<Item> newItems) {
int pos = listView.getFirstVisiblePosition();
View cell = listView.getChildAt(pos);
String savedId = adapter.getItemId(pos); // item the user is currently looking at
savedPositionOffset = cell == null ? 0 : cell.getTop(); // current item top offset
// Now we block listView drawing until after setSelectionFromTop() is called
final ListViewPredrawListener predrawListener = new ListViewPredrawListener(listView);
predrawListener.lock();
// We have no idea what changed between items and newItems, the only assumption
// that we make is that item with savedId is still in the newItems list
items = newItems;
notifyDataSetChanged();
// or for ArrayAdapter:
//clear();
//addAll(newItems);
listView.post(new Runnable() {
#Override
public void run() {
// Now we can finally unlock listView drawing
// Note that this code will always be executed
predrawListener.unlock();
int newPosition = ...; // Calculate new position based on the savedId
listView.setSelectionFromTop(newPosition, savedPositionOffset);
}
});
}
I'm working on dictionary application. I have a listview with fast scroll enabled and adapter which implements SectionIndexer. When I'm working with chinese dictionary I have much more sections then when working with west-european languages and have a small issue:
if I stop fast scrolling and while scroll bars are visible begin using
default scrolling my "fast scroll scroller" immideatly moves to
another position (somewhere at the top) and only when I'll get almost
to the end of the list it'll start moving to my position too with much
greater speed.
Is there a reason for such behaviour? If there any way to hide fast scroll bars when using default scrolling (but without disabling the fast scroll)?
thought I'd post here in the hopes that it's not too late.
Note: This is not using an AlphabetIndexer, and I don't think using three collections to manage a list is a good idea, though it is simple, and explains the concept.
Below is a basic example of how to use the callbacks:
public LinkedHashMap<Integer,String> sectionList = new LinkedHashMap<Integer,String>();
public HashMap<Integer,Integer> sectionPositions = new HashMap<Integer, Integer>();
public HashMap<Integer,Integer> positionsForSection = new HashMap<Integer, Integer>();
When you have your "locations" array (pre-ordered), this will create three hashmaps to track things, really simple implementation, really easy to read :
if( locations != null && locations.size() > 0 ) {
//Iterate through the contacts, take the first letter, uppercase it, and use that as a key to reference the alphabetised list constructed above.
for( int i = 0; i < locations.size(); i++ ) {
String startchar =locations.get(i).getStartCharacterForAlphabet();
if( startchar != null ) {
if( sectionList.containsValue(startchar) == false ) {
sectionList.put(Integer.valueOf(i),startchar);
positionsForSection.put(Integer.valueOf(sectionList.size() - 1), Integer.valueOf(i));
}
}
sectionPositions.put(Integer.valueOf(i), sectionList.size() - 1);
}
}
And here are the three callbacks:
#Override
public int getPositionForSection(int section) {
return positionsForSection.get(Integer.valueOf(section)).intValue();
}
#Override
public MyLocation getItem(int position) {
if( locations.size() > position ) {
return locations.get(position);
}
return null;
}
#Override
public int getSectionForPosition(int position) {
return sectionPositions.get(Integer.valueOf(position)).intValue();
}
Hope it helps!
I have a LinearLayout that contains some other views and among those a ListView.
This view is loaded from another one by clicking a button.
This button somehow specify what element in the ListView needs to be the first visible one in the list. The elements that populates the list are retrieved via HTTP from an external server.
The problem is that I can get the Nth element to be the first in the list.
Please note, I do not want to move it form it current position to a new one, I want the list to scroll.
I have tried with setSelected() and scrollTo(x,y) and scrollBy(x,y) but with no luck.
I have also gave a try to this pice of code, as ugly as it is, but I just wanted to try f it was working:
ListView categoryList = (ListView)findViewById(R.id.category_list);
categoryList.post(new Runnable() {
#Override
public void run() {
Log.d(this.getClass().getName(), "CategoryActivity.scrollToIndex: " + CategoryActivity.scrollToIndex);
if(CategoryActivity.scrollToIndex>0){
ListView categoryList = (ListView)findViewById(R.id.category_list);
categoryList.setScrollContainer(true);
categoryList.scrollTo(4, CategoryActivity.scrollToIndex * 50);
categoryList.requestLayout();
}
}
});
And this gave me some success, but the ListView was then behaving crazy in a way I am not even able to describe....
Any idea?
Try to add it to the message queue
categoryList.post(new Runnable() {
public void run() {
categoryList.scrollTo(4, CategoryActivity.scrollToIndex * 50);
}
});
It worked for me in a ScrollView (check this answer).
i made functions that could be useful for others for listview scrolling, they work for me in every android version, emulator and device, here itemheight is the fixed height of view in the listview.
int itemheight=60;
public void scrollToY(int position)
{
int item=(int)Math.floor(position/itemheight);
int scroll=(int) ((item*itemheight)-position);
this.setSelectionFromTop(item, scroll);// Important
}
public void scrollByY(int position)
{
position+=getListScrollY();
int item=(int)Math.floor(position/itemheight);
int scroll=(int) ((item*itemheight)-position);
this.setSelectionFromTop(item, scroll);// Important
}
public int getListScrollY()
{
try{
//int tempscroll=this.getFirstVisiblePosition()*itemheight;// Important
View v=this.getChildAt(0);
int tempscroll=(this.getFirstVisiblePosition()*itemheight)-v.getTop();// Important
return tempscroll;
}catch(Exception e){}
return 0;
}