我在布局中使用HorizontalScrollView
,我需要确定用户已经到达滚动的起点和终点。
对于ListView
我尝试了onScrollListener
,可以找到滚动的起点和终点。
我试图在Scrollview
中做同样的事情,但似乎不可能。有没有其他可能的方法来实现我的需要。
答案 0 :(得分:360)
每个View实例都会调用getViewTreeObserver()
。现在,当持有ViewTreeObserver
的实例时,您可以使用方法OnScrollChangedListener()
向其添加addOnScrollChangedListener()
。
您可以查看有关此课程here的更多信息。
它让您了解每个滚动事件 - 但没有坐标。您可以在侦听器中使用getScrollY()
或getScrollX()
来获取它们。
scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int scrollY = rootScrollView.getScrollY(); // For ScrollView
int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
// DO SOMETHING WITH THE SCROLL COORDINATES
}
});
答案 1 :(得分:39)
这可能非常有用。
使用NestedScrollView
代替ScrollView
。支持库23.1引入了OnScrollChangeListener
到NestedScrollView
。
所以你可以这样做。
myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
//Do something
}
});
答案 2 :(得分:17)
这是我编写的一个派生的HorizontalScrollView,用于处理有关滚动和滚动结尾的通知。当用户在用户放手后完全减速时,当用户停止主动滚动和时,它会正确处理:
public class ObservableHorizontalScrollView extends HorizontalScrollView {
public interface OnScrollListener {
public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
public void onEndScroll(ObservableHorizontalScrollView scrollView);
}
private boolean mIsScrolling;
private boolean mIsTouching;
private Runnable mScrollingRunnable;
private OnScrollListener mOnScrollListener;
public ObservableHorizontalScrollView(Context context) {
this(context, null, 0);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_MOVE) {
mIsTouching = true;
mIsScrolling = true;
} else if (action == MotionEvent.ACTION_UP) {
if (mIsTouching && !mIsScrolling) {
if (mOnScrollListener != null) {
mOnScrollListener.onEndScroll(this);
}
}
mIsTouching = false;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (Math.abs(oldX - x) > 0) {
if (mScrollingRunnable != null) {
removeCallbacks(mScrollingRunnable);
}
mScrollingRunnable = new Runnable() {
public void run() {
if (mIsScrolling && !mIsTouching) {
if (mOnScrollListener != null) {
mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
}
}
mIsScrolling = false;
mScrollingRunnable = null;
}
};
postDelayed(mScrollingRunnable, 200);
}
if (mOnScrollListener != null) {
mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
}
}
public OnScrollListener getOnScrollListener() {
return mOnScrollListener;
}
public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
this.mOnScrollListener = mOnEndScrollListener;
}
}
答案 3 :(得分:3)
如果您想知道视图的滚动位置,那么您可以在View类上使用以下扩展功能:
d = {}
for item in l:
d.setdefault(item, []).append(item)
答案 4 :(得分:3)
您可以使用NestedScrollView
代替ScrollView
。但是,当使用Kotlin Lambda时,它不会知道您想要NestedScrollView的setOnScrollChangeListener
而不是View(API级别23)上的那个。您可以通过将第一个参数指定为NestedScrollView来解决此问题。
nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
答案 5 :(得分:1)
// --------Start Scroll Bar Slide--------
final HorizontalScrollView xHorizontalScrollViewHeader = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewHeader);
final HorizontalScrollView xHorizontalScrollViewData = (HorizontalScrollView) findViewById(R.id.HorizontalScrollViewData);
xHorizontalScrollViewData.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int scrollX; int scrollY;
scrollX=xHorizontalScrollViewData.getScrollX();
scrollY=xHorizontalScrollViewData.getScrollY();
xHorizontalScrollViewHeader.scrollTo(scrollX, scrollY);
}
});
// ---------End Scroll Bar Slide---------
答案 6 :(得分:1)
您可以定义一个自定义ScrollView类,并在添加时添加一个被调用的接口。像这样:
public class ScrollChangeListenerScrollView extends HorizontalScrollView {
private MyScrollListener mMyScrollListener;
public ScrollChangeListenerScrollView(Context context) {
super(context);
}
public ScrollChangeListenerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollChangeListenerScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOnMyScrollListener(MyScrollListener myScrollListener){
this.mMyScrollListener = myScrollListener;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(mMyScrollListener!=null){
mMyScrollListener.onScrollChange(this,l,t,oldl,oldt);
}
}
public interface MyScrollListener {
void onScrollChange(View view,int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}
}
答案 7 :(得分:1)
除了可接受的答案外,您还需要持有一个侦听器的引用,并在不需要时将其删除。否则,您将为ScrollView和内存泄漏(在接受的答案的注释中提到的)得到空指针异常。
您可以在活动/片段中实现OnScrollChangedListener。
MyFragment : ViewTreeObserver.OnScrollChangedListener
准备好视图后将其添加到scrollView。
scrollView.viewTreeObserver.addOnScrollChangedListener(this)
在不再需要时删除监听器(即onPause())
scrollView.viewTreeObserver.removeOnScrollChangedListener(this)
答案 8 :(得分:0)
Kotlin 用户正在寻找正常 ScrollView
实现的解决方案:
作为 this answer 的扩展,我创建了一个自定义视图,很好地解决了我的问题。
视图(创建一个新的 Kotlin 文件,在第 1 行维护您的包引用):
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.ScrollView
import kotlin.math.abs
class ScrollViewWithEndFunc (
context: Context?,
attrs: AttributeSet?,
defStyle: Int
) : ScrollView(context, attrs, defStyle) {
constructor(context: Context?) : this(context, null, 0) {}
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0) {}
interface OnScrollListener {
fun onScrollChanged(scrollView: ScrollViewWithEndFunc?, x: Int, y: Int, oldX: Int, oldY: Int)
fun onEndScroll(scrollView: ScrollViewWithEndFunc?)
}
private var isScrolling = false
private var isTouching = false
private var scrollingRunnable: Runnable? = null
private var onScrollListener: OnScrollListener? = null
fun setOnScrollListener(onScrollListener: OnScrollListener) {
this.onScrollListener = onScrollListener
}
fun removeOnScrollListener() {
this.onScrollListener = null
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean {
val action = ev.action
if (action == MotionEvent.ACTION_MOVE) {
isTouching = true; isScrolling = true
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (isTouching && !isScrolling) {
onScrollListener?.onEndScroll(this)
}
isTouching = false
}
return super.onTouchEvent(ev)
}
override fun onScrollChanged(x: Int, y: Int, oldX: Int, oldY: Int) {
super.onScrollChanged(x, y, oldX, oldY)
if (abs(oldY - y) > 0) {
scrollingRunnable?.let { removeCallbacks(it) }
scrollingRunnable = Runnable {
if (isScrolling && !isTouching) {
onScrollListener?.onEndScroll(this@ScrollViewWithEndFunc)
}
isScrolling = false
scrollingRunnable = null
}
postDelayed(scrollingRunnable, 200)
}
onScrollListener?.onScrollChanged(this, x, y, oldX, oldY)
}
}
XML 视图实现:
<your.package.here.ScrollViewWithEndFunc
android:id="@+id/scrollview_main_dashboard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
Activity/Fragment 实现:
scrollviewMainDashboard.setOnScrollListener(object : ScrollViewWithEndFunc.OnScrollListener {
override fun onScrollChanged(scrollView: ScrollViewWithEndFunc?, x: Int, y: Int, oldX: Int, oldY: Int) { }
override fun onEndScroll(scrollView: ScrollViewWithEndFunc?) {
/* Scroll ended, handle here */
})