I found a solution that works excellently and can scroll the ListView
without problems:
ListView lv = (ListView)findViewById(R.id.myListView); // your listview inside scrollview
lv.setOnTouchListener(new ListView.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
v.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
// Handle ListView touch events.
v.onTouchEvent(event);
return true;
}
});
What this does is disable the TouchEvent
s on the ScrollView
and make the ListView
intercept them. It is simple and works all the time.
You shouldn't put a ListView
inside a ScrollView
because the ListView
class implements its own scrolling and it just doesn't receive gestures because they all are handled by the parent ScrollView
. I strongly recommend you to simplify your layout somehow. For example you can add views you want to be scrolled to the ListView
as headers or footers.
UPDATE:
Starting from API Level 21 (Lollipop) nested scroll containers are officially supported by Android SDK. There're a bunch of methods in View
and ViewGroup
classes which provide this functionality. To make nested scrolling work on the Lollipop you have to enable it for a child scroll view by adding android:nestedScrollingEnabled="true"
to its XML declaration or by explicitly calling setNestedScrollingEnabled(true)
.
If you want to make nested scrolling work on pre-Lollipop devices, which you probably do, you have to use corresponding utility classes from the Support library. First you have to replace you ScrollView
with NestedScrollView. The latter implements both NestedScrollingParent and NestedScrollingChild so it can be used as a parent or a child scroll container.
But ListView
doesn't support nested scrolling, therefore you need to subclass it and implement NestedScrollingChild
. Fortunately, the Support library provides NestedScrollingChildHelper class, so you just have to create an instance of this class and call its methods from the corresponding methods of your view class.
I have also one solution. I always use this method. Try this
<ScrollView
android:id="@+id/createdrill_scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="15dp" >
<net.thepaksoft.fdtrainer.NestedListView
android:id="@+id/crewList"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:background="@drawable/round_shape"
android:cacheColorHint="#00000000" >
</net.thepaksoft.fdtrainer.NestedListView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="15dp" >
<net.thepaksoft.fdtrainer.NestedListView
android:id="@+id/benchmarksList"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_weight="1"
android:background="@drawable/round_shape"
android:cacheColorHint="#00000000" >
</net.thepaksoft.fdtrainer.NestedListView>
</LinearLayout>
</ScrollView>
NestedListView.java class:
public class NestedListView extends ListView implements OnTouchListener, OnScrollListener {
private int listViewTouchAction;
private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;
public NestedListView(Context context, AttributeSet attrs) {
super(context, attrs);
listViewTouchAction = -1;
setOnScrollListener(this);
setOnTouchListener(this);
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, -1);
}
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int newHeight = 0;
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY) {
ListAdapter listAdapter = getAdapter();
if (listAdapter != null && !listAdapter.isEmpty()) {
int listPosition = 0;
for (listPosition = 0; listPosition < listAdapter.getCount()
&& listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {
View listItem = listAdapter.getView(listPosition, null, this);
//now it will not throw a NPE if listItem is a ViewGroup instance
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
listItem.measure(widthMeasureSpec, heightMeasureSpec);
newHeight += listItem.getMeasuredHeight();
}
newHeight += getDividerHeight() * listPosition;
}
if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {
if (newHeight > heightSize) {
newHeight = heightSize;
}
}
} else {
newHeight = getMeasuredHeight();
}
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getAdapter() != null && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {
if (listViewTouchAction == MotionEvent.ACTION_MOVE) {
scrollBy(0, 1);
}
}
return false;
}
}
You have to just replace your <ScrollView ></ScrollView>
with this Custom ScrollView
like <com.tmd.utils.VerticalScrollview > </com.tmd.utils.VerticalScrollview >
package com.tmd.utils;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;
public class VerticalScrollview extends ScrollView{
public VerticalScrollview(Context context) {
super(context);
}
public VerticalScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
return false; // redirect MotionEvents to ourself
case MotionEvent.ACTION_CANCEL:
Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_UP:
Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
return false;
default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
return true;
}
}
I had a same issue and while googling I found your question. Yes marked answer worked for me also but there was some issue.
Anyways I found another solution.
which works perfectly without doing any jugglery.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With