Create a scrollable LinearLayout without a ScrollView - android

I spent a good part of a day looking into scrolling content in Android. However, in my app I need to apply a custom type of scrolling that contains among other things a listview. A listview has its own scrolling and a scrollview should never be used with a list view together.
But there must be some intrinsic scroll functionality for views. The documentation on View does indicate support but the methods provided do not seem to provide any listeners to detect scrolling.
A scrollview also is built on a FrameLayout which would also be problematic in my app.
You can make a LinearLayout scrollable with:
android:isScrollContainer="true"
but this doesn't seem to do anything.
In fact you can even set the scrollbars for a LinearLayout:
android:scrollbars="vertical"
but that also doesn't seem to do anything.
I don't want to control scrolling but rather have a listener that detects scrolling on the screen regardless whether a listview, framelayout or any other control is visible. I guess what I am looking for is writing my own custom ScrollView control but without the limitations of its FrameLayout and all the unnecessary overhead that I don't need. Any suggestions?

On way to resolve above problem is to have 2 ScrollView, one is the outer ScrollView that contain all the view and other inner Scrollview instead of listview and adding the items inside LinearLayout dynamically(Inside inner ScrollView)

Actually after doing some research, I come up with a solution for this problem:
At first I want to explain the problem in a very simple way.
1.LinearLayout will be scrollable.
2.To do this we can use ScrollView but sometimes we need to use ListView inside LinearLayout.
3.We know that inside ScrollView we cannot use another scrollview like ListView
How to solve that?
ListView is scrollable inherently so we can add header and footer in the ListView. As a conclusion:
1.Create Layout header.xml and footer.xml and list.xml
2.Find the ListView reference from list.xml in the main activity and dynamically add header and footer in the ListView reference.

Here is what seems like a viable solution which I tested out but not thoroughly. Create a custom control that extends a LinearLayout and then override the dispatchTouchEvent:
#Override
public boolean dispatchTouchEvent(MotionEvent event)
{
MotionEvent vtev = MotionEvent.obtain(event);
final int actionMasked = event.getActionMasked();
float x = event.getRawX();
float y = event.getRawY();
switch (actionMasked)
{
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int z = 0;
z++;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_POINTER_DOWN:
{
final int index = event.getActionIndex();
break;
}
case MotionEvent.ACTION_POINTER_UP:
break;
}
boolean ret = super.dispatchTouchEvent(event);
return ret;
}
}
You can then place child controls inside this custom LinearLayout and detect motion events, even when ListViews and ScrollViews are present. This doesn't mess with their own scrolling.

Related

android: RecyclerView inside a ScrollView

I have a RecyclerView wrapped in a LinearLayout and it works perfectly as expected. I can see all the data in the RecyclerView as populated. So far so good.
When I wrap the LinearLayout in a ScrollView, the RecyclerView goes blank. I do not see anything inside RecyclerView. Why? How to make this work.
The page is one of the tabs in a ViewPagerIndicator, so everything in that tab needs to be in a ScrollView.
Thanks for all the help.
Set this property for the ScrollView,
android:fillViewport="true"
ScrollView will extend itself to fill the contents
After checking implementation, the reason appears to be the following. If RecyclerView gets put into a ScrollView, then during measure step its height is unspecified (because ScrollView allows any height) and, as a result, gets equal to minimum height (as per implementation) which is apparently zero.
You have couple of options for fixing this:
Set a certain height to RecyclerView
Set ScrollView.fillViewport to true
Or keep RecyclerView outside of ScrollView. I my opinion, this is the best option by far. If RecyclerView height is not limited - which is the case when it's put into ScrollView - then all Adapter's views have enough place vertically and get created all at once. There is no view recycling anymore which kinda breaks the purpose of RecyclerView.
Nothing helped me except this:
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
rv.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
I got this answer there. Thank you Piyush Gupta for that.
Hope this helps :
Add this line to your recyclerView xml :
android:nestedScrollingEnabled="false"
Try it ,recyclerview will be smoothly scrolled with flexible height inside scrollview .

ListView inside ScrollView for big List Item Android

I am using ListView inside ScrollView. But as we all know, It creates scrolling problems.
As per this link : Android list view inside a scroll view
I used that code. No doubt it is working fine. But now, problem occurs when any List Item has big content which will take 2-3 lines.Check out image below for wide view : It has actually 4 options but because of scroll problem, it is showing 2 options.
I have also tried below extra code for height but It also dint work. What can be the solution for this ? I don't want to use header and footer as it is not suitable for our application. I want to prevent this Scroll problem and I want to display options perfectly.
listView.getPaddingTop() + listView.getPaddingBottom(); -> gives 0
out this code for your child listview . It will solve the scrolling issue.
mListView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mScrollView.requestDisallowInterceptTouchEvent(true);
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_UP:
mScrollView.requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});

Multiple Scrollable TextViews inside a ScrollView

I have a ScrollView that contains a complex LinearLayout with various elements, among which there are several TextView with maxHeight defined and a long text inside. I need to enable scrolling the text inside the TextView when touch-and-dragging inside it, and the normal scroll of the ScrollView when touch-and-drag outside of them.
Is there a proper and elegant way to do this - if at all possible - or do I have to get dirty and start overriding the onTouchEvent handlers on all elements involved?
UPD 1: Judging from this there is no elegant solution, and the UI will have to be rethinked to include only one layer of scrollable elements.
So it's not pretty, but you can extend from TextView and block the scrollview from scrolling. It's not as nice unless your textviews are large enough. After using this solution we found that it is best if you have a "show more" label that the users can click to show the full thing. Anyway, extend from TextView and override the onTouchEvent method:
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
this.getParent().requestDisallowInterceptTouchEvent(true);
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
this.getParent().requestDisallowInterceptTouchEvent(false);
}
return super.onTouchEvent(ev);
}
A ScrollView is a FrameLayout, meaning you should place one child in it containing the entire contents to scroll; this child may itself be a layout manager with a complex hierarchy of objects. A child that is often used is a LinearLayout in a vertical orientation, presenting a vertical array of top-level items that the user can scroll through.
<ScrollView
android:id="#+id/ScrollView01"
android:layout_height="150px"
android:layout_width="fill_parent">
<TextView
android:id="#+id/TEXT_VIEW"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header This text view should act as header" />
</ScrollView>
you can see the APIdemos for more information, there is a a view of lot of text's with their own stylized scrollview in the same windows. For more inforation you can visit:
http://developer.android.com/resources/samples/ApiDemos/index.html

android: listview inside a scrollView doesn't work?

I am having a listview inside a scrollview, but the problem is that the scrollview is scrolling but listview is not scrolling. I think this is due to that scrollView.
Can somebody who has a working solution post it here as reference?
Generally, you cannot put scrollable things inside other scrollable things, where they scroll in the same direction, and have the results be reliable. Occasionally this works (e.g., WebViews in a ViewPager), but that is the exception, not the norm.
Either:
Move the ListView out of the ScrollView, or
Move all the rest of the contents of the ScrollView into the ListView, whether using things like addHeaderView() or my MergeAdapter
If you put your ListView/any scrollable View inside the scrollView it will not work properly because when you touch the screen ,main focus of your touch is on parent view(scrollView ) not the child View (ListView).
ListView must have fixed height as below in your XML file
<ListView android:id="#+id/lv"
android:listSelector="#0f0"
android:layout_width="fill_parent"
android:layout_height="500px" />
In Java file, write below code after setContentView()
lv = (ListView)findViewById(R.id.lv);
lv.setAdapter(your adapter here); // you have to add your adapter here
lv.setOnTouchListener(new ListView.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE)
{
lv.scrollBy(0, 1);
}
return false;
}
});
Make these changes to your code and test it. After too many experiments i written this code. It is working 100% fine.

Android: Synchronized scrolling of two different views

I have a tricky problem related to synchronized scrolling of two
different views.
I've made my own custom grid view widget, which has "sticky" views to
the left and top that only in one direction with the grid. Think of a
calendar where you have times at the top, dates at the left, and when
you scroll horizontally through time, the date view should stay put,
and when you scroll vertically through the dates, the time view should
stay put.
The grid itself is implemented using a nested horizontal scrollview in
a vertical scrollview. The grid is working great, so no problem there.
Since the sticky views are not in the actual grid, I have overriden
onScrollChanged in the grid scrollviews and programatically call
scrollTo on the sticky views when the user scrolls the grid.
That works as expected, except that there is a slight time offset as
to when the two different views start scrolling and ends scrolling. It
makes sense when you consider that the scrolling likely is executed
linearly on the UI thread I suppose..
All the views are scroll views, and I have enabled smooth scrolling
and used smoothScrollTo, etc, instead to try to improve this, but it's
the same problem nonetheless. The problem is especially noticeable on
larger screens, such as the Samsung Galaxy Tab, whereas it's hardly
noticeable on small-medium screen devices.
Any help is appreciated! If there is an easy fix, great..if it means
new design (that meets the sticky view usecase above), then so be it.
Code to trigger prog. scroll, same for horizontal
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
mListener.onScrollY(y);
super.onScrollChanged(x, y, oldx, oldy);
}
// which leads to,
// Handle vertical scroll
public void onScrollY(final int y) {
mCurrentY = y;
mVerticalScroll.smoothScrollTo(0, y);
}
XML layouts below, if that's of any help
The actual grid, which is a horizontal scroll view wrapped in a vertical scroll view and the grid items are added vertically in the nested linearlayout
>
< com.....VerticalScrollView
android:id="#+id/gridscroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/timescroll"
android:layout_toRightOf="#id/vertscroll"
android:layout_alignTop="#id/vertscroll"
android:layout_marginLeft="2dp" android:scrollbars="none"
android:fadingEdge="none">
< com....HorizScrollView
android:id="#+id/horizscroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:fadingEdge="none">
< LinearLayout android:id="#+id/grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
< /LinearLayout>
< /com.....HorizScrollView>
< /com.....VerticalScrollView>
The horizontal sticky view
< com.....GridTimeScrollView
android:id="#+id/timescroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:fadingEdge="none">
< LinearLayout android:id="#+id/timelist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" />
< /com.....GridTimeScrollView>
The vertical sticky view
< com....GridVertListScrollView
android:id="#+id/vertscroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:fadingEdge="none">
< LinearLayout
android:id="#+id/vertitemlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" />
< /com.....GridVertListScrollView>
First of all, I think you should be aware of this: ScrollView Inside ScrollView
In short: using scrollviews inside scrollviews is a bad thing that breaks many optimizations.
Now, onto your question.
I've had a similar need to what you described. I ended up implementing a custom view and its onDraw method. This was in part because I was drawing something not trivial and you may not have to do it.
Anyway, I believe that your best option is:
Implement a custom view that extends relative layout
create the layout of this view with the top, left and "main" views that will be the scrollable components
add a OnGestureListener to this view and pass touch events in your activity into the custom view
when your gesture listener detects a fling or a scroll, invoke scrollBy in each of the scrolling views. When you do this, if you want the top view to scroll horizontally only, pass 0 as the vertical scroll distance.
In order to implement smooth fling movements, you need to create a scroller (in your custom view). When the gesture listener detects a fling event, set the scroller up. Then, override your custom view's computeScroll() method and update the scroll in each of child views. Check this example to know how to implement it. I apologize, I will try to post a better example when possible. Check my code below... it's simpler :)
Update: sample code
#Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
if (!scrolledLastFrame) {
lastX = scroller.getStartX();
lastY = scroller.getStartY();
}
int dx = scroller.getCurrX() - lastX;
int dy = scroller.getCurrY() - lastY;
lastX = scroller.getCurrX();
lastY = scroller.getCurrY();
doScroll(dx, dy);
scrolledLastFrame = true;
} else {
scrolledLastFrame = false;
}
}
Don't use smoothScrollTo, use scrollTo instead. smoothScrollTo will animate a scroll from its current position to the position you want, because you want them to be synced, you want the other scrollview to be instantly exactly where the other scrollview is, scrollTo does that.
Looking at the answers. Why don't you handle it with a OnTouch Listener. When the touch event is MOVE you call.
public void onScrollY(final int y) {
mCurrentY = y;
mVerticalScroll.smoothScrollTo(0, y);
}
with the value of getVerticalScroll on your other ScrollView. That's pretty easy. And return true so TouchEvents will be handled further.
Here's what it could look like
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
lv2.onScrollY(lv.getScrollY());
break;
default:
break;
}
return false;
}
Simple right?
Not sure if it's exactly what you wanted. But could you elaborate exactly what you're trying to achieve. Not in the sense of layout but in practice..whats the point of your UI?

Categories

Resources