I have a bunch of view in ScrollView.
Some of them are TextInputEditText.
When an error occurs I use setError for particular TextInputEditText.
And I have such effect.
But my layout is long, and I need to scroll to view with an error.
Code:
protected void showInputError(TextInputEditText edtText, #StringRes int resId) {
edtText.setError(getString(resId));
scrollView.post(() -> {
scrollView.scrollTo(0, edtText.getTop());
});
}
All works fine first time. But the app does the same scrolling after any other scrolling.
I mean,
set an error to view
scroll programmatically to this view
scroll manually to another place. Application scrolls again to view from step 2.
The main problem: Application scrolls back to view when user scrolls manually (after applying of scrollTo)
Please, give a hint how to overcome this
Related
I have a ScrollView with multiple TextView children nested inside its layout. I want the text in these children to be selectable (android:textIsSelectable="true"), so the user can use the copy, share and select all actions. But when one of the children receives focus (from touch or long press), it causes the parent ScrollView to scroll to the focused child. I suppose this is a feature, but it introduces three problems for me.
When text is selected with a long press, it causes the text to scroll and as a result the children above the focused child will scroll outside of visible bounds.
The position of the selected text will change and will not appear on the screen where the user made the long press, which is unintuitive.
My app responds to double-tap gestures. The first touch (or tap) of a double-tap will cause the child that received the touch event to receive focus and thus cause a scroll. Apparently, the scroll will cause the gesture to fail; the child is touched, the parent scrolls, the child is touched again, but the double-tap gesture is not detected.
Making the children unfocusable prevents the scroll, but then the text cannot be selected. So how can I have TextView views nested inside a ScrollView with selectable text, but prevent scrolling when one of the views receives focus?
While searching for possible solutions to this problem, I found this article https://programmersought.com/article/8487993014/. It is not directly related to this problem, but one specific part where the article shows some source code from ScrollView caught my attention:
public void requestChildFocus(View child, View focused) {
if (focused != null && focused.getRevealOnFocusHint()) {
If (!mIsLayoutDirty) {//This boolean value marks whether the layout has been completed. If it is completed, it is false.
scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
}
//super method [handling FOCUS_BLOCK_DESCENDANTS case] is called after scrolling on top.
//So setting the android:descendantFocusability="blocksDescendants" attribute to the ScrollView is invalid.
super.requestChildFocus(child, focused);
}
Here you can see that the ScrollView will attempt to scroll to the focused child if focused != null. So to disable this behaviour, create a subclass of ScrollView and override this method like so:
package com.test
// imports here...
public class MyScrollView extends ScrollView {
// constructors here...
#Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, null);
}
}
By ignoring the focused parameter and passing null to the super implementation, the scroll will never occur, but the parent will still request the child to receive focus and therefore text can still be selected.
The only thing left to do is replace the ScrollView parent in the layout file with the custom implementation defined above.
EDIT
The solution was tested and worked fine on API 30, but when I tested it on API 25 a NPE is thrown and the app crashes. The Android Docs recommend to use the NestedScrollView view instead for vertical scrolling, but the app still crashes. I looked at the source code for requestChildFocus(View, View) and it is slightly different:
#Override
public void requestChildFocus(View child, View focused) {
if (!mIsLayoutDirty) {
scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}
private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
/* Offset from child's local coordinates to ScrollView coordinates */
offsetDescendantRectToMyCoords(child, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
if (scrollDelta != 0) {
scrollBy(0, scrollDelta);
}
}
The method scrollToChild(View) is made private and so we cannot override the default implementation (because why would anyone ever want to do that, right?), but scrollBy(int, int) is public. So instead of overriding requestChildFocus(View, View) in the MyScrollView class, override scrollBy(int, int) and make it do nothing. This was tested on both API 30 and 25 and worked as intended without crashing. I later tried to revert back to extending ScrollView and it still works. So you just have to override the method, the supertype does not matter.
When a button is clicked, I would like the app to smoothly scroll to the bottom of a listview. Currently this is my implementation (kotlin)
listview.post { listview.smoothScrollToPosition(adapter.count - 1) }
However, this does not always work properly: Some list items contain some expandable parts (i.e. view whose visibility can be VISIBLE or GONE), which initially are hidden.
When these parts are expanded however, the function sometimes does not scroll to the end, but to a position before the end.
How can I reliably scroll to the end of the listview?
Try following. it will solve your problem, i tried it and it works great.
listView.post(new Runnable(){
public void run() {
listView.setSelection(listView.getCount() - 1);
}});
add android:transcriptMode="alwaysScroll"
to the ListView block in xml
I try to implement following behavior of my app:
I've got list of views and show them in scrollview partly.
If user scroll and reaches bottom of scrollview, I add one more view below last view;
If user scroll up and reaches top of scrollview, I add view above the first view.
My problem is that when I add view above the first view it shift all views in layout down.
My question is: how to add view above view in hidden area, so user could have a look by scrolling up?
I have a ScrollView where there are a number of EditText fields.
When i call requestFocus() on any of those fields, the EditText fields comes into view as the scroll view scrolls by itself, even when the soft keyboard is up.
The problem lies in the fact that i have had to add a separate view above the soft keyboard to accommodate a clear button, and it is that custom view that is hiding the edit text field in focus.
Now, i have to manually scroll the scroll view to see the edit text field in focus.
Please help me to show the edit text field in focus above the custom view which is placed on top of the soft keyboard.
Is there any way to do so ?
EDIT:: This is how i solved this problem.
scrollView.post(new Runnable() {
public void run() {
scrollView.scrollTo(0, scrollView.getBottom()-200);
}
});
and gradually went on decreasing the amount i am subtracting from the scrollView.getBottom() call. This worked fine for me.
In your manifest.xml file, for your activity just add this line:
android:windowSoftInputMode="adjustPan"
I want to scroll to the end of the view. Currently i am trying to scroll the scrollview to the end of the latest added view. I tried with setscrolly, smoothscrollto. both works but the problem is both wont scroll to the end, it just scrolls to the last-1 view. Please suggest me how to overcome this.
I got the reason why it was not scrolling to the last view and was stopping at the last-1 view. I was not smoothScrollTo(x,y) in a post thread of the view. Since my scrollview is not refreshed when the last view is added it was not scrolling to the last view.
view.post(new Runnable() {
public void run() {
scrollview.smoothScrollTo(x,y);
});
Try to use transcript mode param of list view. It provides scrolling to new items automatically:
getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);