I was wondering, how to check whether the current ScrollView is scrollable? It seems that, there isn't public method called canScroll or isScrollable in ScrollView.
Scrollable : You can move the ViewGroup inside the ScrollView up and down, and the scroll bar will be visible when you move it up and down. So, if there is only little rows in the ScrollView, and they can fit inside single screen, ScrollView is not scrollable then.
You can do some little math to calculate the views raw height and the height of the content. If the difference of this heights is < 0 the view is scrollable.
To calculate the raw height you can use View.getMeasuredHeight().
Because ScrollView is a ViewGroup and has max one child, get the height of that child with ViewGroup.getChildAt(0).getHeight();
Use a ViewTreeObserver to get the heights, because it will be called at the moment the layout / view is changing the visibility, otherwise the heights could be 0.
ScrollView scrollView = (ScrollView)findViewById(R.id...);
ViewTreeObserver observer = scrollView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int viewHeight = scrollView.getMeasuredHeight();
int contentHeight = scrollView.getChildAt(0).getHeight();
if(viewHeight - contentHeight < 0) {
// scrollable
}
}
});
scrollView.viewTreeObserver
.addOnGlobalLayoutListener {
val isScrollable = scrollView.canScrollVertically(1)
}
I think I might be missing something, but shouldn't it be as simple as checking if
scrollView.getHeight() >= parentView.getMeasuredHeight()
you might actually need: scrollView.getChildAt(0).getHeight() and/or parentView.getHeight() instead, but the idea is the same.
Related
I make some custom view, and now im stuck with one specific issue.
Now ill try to explain: in my case the view hierarchy is
<view extends linear layout>
<child-view extends relative layout>
<some content>
<relative-layout>
<progressbar> (align text view right, match parent)
<textview> (align parent right, wrap content) <- there's i have to make some changes
<relative-layout>
<child-view>
<...>
<view>
the child items are added according to the data coming from the server.
I want to make all textviews with the same width - width of textview which content length is max. How can I implement this? I tried to calculate width in onMeasure callback, and then set width to another views, but getMeasuredWidth() and getWidth() returns 0 every time. I Heard something about ViewTreeObserver, but i can not understand how to use it right. Thanks in advance!
Try this,
int maxWidth = 0;
....
tv1.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = layout.getMeasuredWidth();
if(width > maxWidth){
maxWidth = width;
// update all textView width with max width.
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
this.layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
this.layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
}
});
same code for tv2,tv3 and tv4. it will work.
I have a LinearLayout (vertical) with two child views in it. The 1st one is a ScrollView and the 2nd one is another layout with Visibility.GONE, its size is not fixed (determined by its children).
<LinearLayout vertical>
<ScrollView> ... </ScrollView>
<AnotherLayout visibility=GONE height=wrap_content> ... </AnotherLayout>
</LinearLayout>
At some point of time I want to show AnotherLayout. But, once it pops up, I also want to adjust the scrolling of my ScollView one. For this, I need to know the size of this AnotherLayout.
I'm doing something like that:
int oldHeight = scrollArea.getHeight();
linearLayout.setVisibility(VISIBLE);
int newHeight = scrollArea.getHeight();
But oldHeight and newHeight are still the same.
How can I calculate the new height?
The two dimensions are the same because visibility change took time and the line of code was run before that so it returns the same.
You can use a visibility listener to calculate the dimension after visibility change , You may use that
linearLayout.setTag(linearLayout.getVisibility());
linearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int newVis = linearLayout.getVisibility();
if((int)linearLayout.getTag() != newVis)
{
linearLayout.setTag(linearLayout.getVisibility());
//visibility has changed
int newHeight = scrollArea.getHeight();
}
}
});
In scrollview, if I add any view in middle, normally all the views below the added view scrolls downside. But I want to scroll the top views of added view to upside without disturbing the bottom views. Is it possible in scrollview , please help me ?
In the figure , If view 4 was added , then view 1 has to be scrolled upwards , without changing the positions of view 2 and view 3.
You can probably get the height of the view you are adding with and then scroll the scrollview manually that many pixels
scrollView.scrollBy(0, viewAdded.getHeight())
I've been wanting to try this question for quite some time, I finally got the chance today. The method is pretty simple (in fact, #dweebo already mentioned it earlier) - we move the ScrollView up as we add the view. For getting precise (and valid) dimensions when adding, we use a ViewTreeObserver. Here's the code you can get hints from:
// Getting reference to ScrollView
final ScrollView scrollView = (ScrollView) findViewById(R.id.scrollView);
// Assuming a LinearLayout container within ScrollView
final LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
// The child we are adding
final View view = new View(ScaleActivity.this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
view.setLayoutParams(params);
// Finally, adding the child
parent.addView(view, 2); // at index 2
// This is what we need for the dimensions when adding
ViewTreeObserver viewTreeObserver = parent.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
parent.getViewTreeObserver().removeOnPreDrawListener(this);
scrollView.scrollBy(0, view.getHeight());
// For smooth scrolling, run below line instead
// scrollView.smoothScrollBy(0, view.getHeight())
return false;
}
});
How can I check if a ScrollView is higher than the screen? When the content of a ScrollView fits on the screen, the ScrollView isn't scrollable, when it's contents exceed the screen height it's scrollable.
How can I check the condition of a ScrollView in that regard?
This is the code from ScrollView, which is private, but can be adapted to be used outside of the class itself
/**
* #return Returns true this ScrollView can be scrolled
*/
private boolean canScroll() {
View child = getChildAt(0);
if (child != null) {
int childHeight = child.getHeight();
return getHeight() < childHeight + mPaddingTop + mPaddingBottom;
}
return false;
}
Too late, but I'm using the following code and it looks more safe for me:
if (view.canScrollVertically(1) || view.canScrollVertically(-1)) {
// you code here
}
A ScrollView always has 1 child. All you need to do is get the height of the child
int scrollViewHeight = scrollView.getChildAt(0).getHeight();
and Calculate Height of Your Screen
if both are equal(or scrollView Height is more) then it fits on your screen.
In my case, I was checking to see if my scrollView(which contained text) was scrollable vertically when the activity was created. On phones, it would scroll but on tablets it couldn't. canScrollVertically was returning me incorrect value because it couldn't be determined yet. I fixed this issue by calling it in the OnGlobalLayoutListener.
(Kotlin)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Must use onGlobalLayout or else canScrollVertically will not return the correct value because the layout hasn't been made yet
scrollView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// If the scrollView can scroll, disable the accept menu item button
if ( scrollView.canScrollVertically(1) || scrollView.canScrollVertically(-1) )
acceptMenuItem?.isEnabled = false
// Remove itself after onGlobalLayout is first called or else it would be called about a million times per second
scrollView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
My use case was displaying terms of use. I didn't want the accept button to be enabled until the user scrolled to the bottom. I know this is late but i hope this resolves some confusion about canScrollVertically
I would like to set the Height of my ScrollView per Code dynamically cause my ScrollView is actually higher then it shall be (empty space at bottom).
My thoughts were, that I could get the Heights of all Controls within the the ScrollView, sum-up them and then I could set that Height to my ScrollView.
What I tried is following code:
protected override void OnStart()
{
base.OnStart();
SetScrollViewSize();
}
private void SetScrollViewSize()
{
var root = FindViewById<ScrollView>(Resource.Id.root);
if (root != null)
{
var controls = GetSelfAndChildrenRecursive(root); //Gives me all Controls in the root (The ScrollView)
int heightOfAllControlsTogether = 0;
foreach (ViewGroup control in controls)
{
heightOfAllControlsTogether += control.Height;
}
ViewGroup.LayoutParams parameters = new ViewGroup.LayoutParams(root.Width, heightOfAllControlsTogether);
root.LayoutParameters = parameters;
}
}
The Heights and MeasuredHeights are always 0 (zero) - (I know it needs to be rendered first, but what would be the right place then?) and I'm not even sure if my approach would work.
Any Help would be appreciated!
Well I found out why.
The Background-Graphic was too high. That was creating the empty space at the bottom.
THis is an example to set scrollview height based on total height of Children:
private void setScrollViewHeightBasedOnChildren() {
int size = layoutRoot.getChildCount();
LinearLayout item = (LinearLayout) layoutRoot.getChildAt(0);
item.measure(0, 0);
int height = item.getMeasuredHeight();
//Reset size of ScrollView
scroll.setMinimumHeight(height / size);
scroll.invalidate();
}
See details: Set the height of ScrollView equal total height of children