나는 ScrollView
Android 에서을 사용 하고 있으며의 보이는 부분 ScrollView
이 Scrollview
. 모든 “셀”은 동일한 높이입니다. 그래서 내가하려는 ScrollView
것은 스크롤 된 후 제자리에 스냅하는 것입니다 .
현재 사용자가를 터치하고 ScrollView
스크롤을 시작하고 거기에서 작업을 시작했을 때 감지 하고 있지만 버그가 상당히 많습니다. 사용자가 그냥 튕겨서 스크롤 한 다음 감속 할 때도 작동해야합니다.
iPhone에는 비슷한 기능이 있으며 스크롤이 끝나면 didDecelerate
원하는 코드를 할 수 있습니다 ScrollView
. Android에 그런 것이 있습니까? 아니면 더 나은 방법을 찾기 위해 볼 수있는 코드가 있습니까?
Android 문서를 살펴 보았지만 그런 것을 찾을 수 없었습니다.
답변
최근에 설명하신 기능을 구현해야했습니다. 내가 한 일은 onTouchEvent가 처음 트리거 될 때 getScrollY ()가 반환 한 값을 변수 newCheck에 정의 된 시간 후에 반환 된 값과 비교하여 ScrollView가 스크롤을 중지했는지 Runnable을 확인하는 것이 었습니다 .
아래 코드 (작동 솔루션)를 참조하십시오.
public class MyScrollView extends ScrollView{
private Runnable scrollerTask;
private int initialPosition;
private int newCheck = 100;
private static final String TAG = "MyScrollView";
public interface OnScrollStoppedListener{
void onScrollStopped();
}
private OnScrollStoppedListener onScrollStoppedListener;
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
scrollerTask = new Runnable() {
public void run() {
int newPosition = getScrollY();
if(initialPosition - newPosition == 0){//has stopped
if(onScrollStoppedListener!=null){
onScrollStoppedListener.onScrollStopped();
}
}else{
initialPosition = getScrollY();
MyScrollView.this.postDelayed(scrollerTask, newCheck);
}
}
};
}
public void setOnScrollStoppedListener(MyScrollView.OnScrollStoppedListener listener){
onScrollStoppedListener = listener;
}
public void startScrollerTask(){
initialPosition = getScrollY();
MyScrollView.this.postDelayed(scrollerTask, newCheck);
}
}
그런 다음 나는 :
scroll.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
scroll.startScrollerTask();
}
return false;
}
});
scroll.setOnScrollStoppedListener(new OnScrollStoppedListener() {
public void onScrollStopped() {
Log.i(TAG, "stopped");
}
});
BTW 나는 내 앱에서 이것을하기 위해 다른 답글의 아이디어를 거의 사용하지 않았습니다. 도움이 되었기를 바랍니다. 질문이 있으시면 언제든지 문의하십시오. 건배.
답변
다음은 ScrollView에서 OnEndScroll 이벤트 버그가 누락 된 IMHO에 대한 또 다른 수정 사항입니다.
해로운 대답에서 영감을 얻었습니다 . 이 클래스를 프로젝트에 드롭하고 (자신의 것과 일치하도록 패키지 변경) 아래 xml을 사용하십시오.
package com.thecrag.components.ui;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;
public class ResponsiveScrollView extends ScrollView {
public interface OnEndScrollListener {
public void onEndScroll();
}
private boolean mIsFling;
private OnEndScrollListener mOnEndScrollListener;
public ResponsiveScrollView(Context context) {
this(context, null, 0);
}
public ResponsiveScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ResponsiveScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void fling(int velocityY) {
super.fling(velocityY);
mIsFling = true;
}
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (mIsFling) {
if (Math.abs(y - oldY) < 2 || y >= getMeasuredHeight() || y == 0) {
if (mOnEndScrollListener != null) {
mOnEndScrollListener.onEndScroll();
}
mIsFling = false;
}
}
}
public OnEndScrollListener getOnEndScrollListener() {
return mOnEndScrollListener;
}
public void setOnEndScrollListener(OnEndScrollListener mOnEndScrollListener) {
this.mOnEndScrollListener = mOnEndScrollListener;
}
}
프로젝트와 일치하도록 패키지 이름을 다시 변경
<com.thecrag.components.ui.ResponsiveScrollView
android:id="@+id/welcome_scroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/welcome_scroll_command_help_container"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/welcome_header_text_thecrag"
android:layout_margin="6dp">
....
</com.thecrag.components.ui.ResponsiveScrollView>
답변
(Horizontal) ScrollView를 서브 클래 싱하고 다음과 같이했습니다.
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
if (Math.abs(x - oldX) > SlowDownThreshold) {
currentlyScrolling = true;
} else {
currentlyScrolling = false;
if (!currentlyTouching) {
//scrolling stopped...handle here
}
}
super.onScrollChanged(x, y, oldX, oldY);
}
항상 마지막 onScrollChanged 이벤트의 차이 인 것처럼 보이기 때문에 SlowDownThreshold에 1의 값을 사용했습니다.
천천히 드래그 할 때 이것이 올바르게 작동하도록하려면 다음을 수행해야합니다.
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
currentlyTouching = true;
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
currentlyTouching = false;
if (!currentlyScrolling) {
//I handle the release from a drag here
return true;
}
}
return false;
}
답변
내 접근 방식은 onScrollChanged ()가 호출 될 때마다 변경된 타임 스탬프에 의해 스크롤 상태를 결정하는 것입니다. 스크롤링의 시작과 끝을 결정하는 것은 매우 쉽습니다. 임계 값을 변경 (100ms 사용)하여 감도를 수정할 수도 있습니다.
public class CustomScrollView extends ScrollView {
private long lastScrollUpdate = -1;
private class ScrollStateHandler implements Runnable {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
if ((currentTime - lastScrollUpdate) > 100) {
lastScrollUpdate = -1;
onScrollEnd();
} else {
postDelayed(this, 100);
}
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (lastScrollUpdate == -1) {
onScrollStart();
postDelayed(new ScrollStateHandler(), 100);
}
lastScrollUpdate = System.currentTimeMillis();
}
private void onScrollStart() {
// do something
}
private void onScrollEnd() {
// do something
}
}
답변
여기에 위의 답변에서 자연스럽게 영감을 얻은 매우 간단하고 깨끗한 또 다른 해결책이 있습니다. 기본적으로 사용자가 제스처를 종료하면 잠시 후 (여기서는 50ms) getScrollY ()가 여전히 변경되고 있는지 확인합니다.
public class ScrollViewWithOnStopListener extends ScrollView {
OnScrollStopListener listener;
public interface OnScrollStopListener {
void onScrollStopped(int y);
}
public ScrollViewWithOnStopListener(Context context) {
super(context);
}
public ScrollViewWithOnStopListener(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
checkIfScrollStopped();
}
return super.onTouchEvent(ev);
}
int initialY = 0;
private void checkIfScrollStopped() {
initialY = getScrollY();
this.postDelayed(new Runnable() {
@Override
public void run() {
int updatedY = getScrollY();
if (updatedY == initialY) {
//we've stopped
if (listener != null) {
listener.onScrollStopped(getScrollY());
}
} else {
initialY = updatedY;
checkIfScrollStopped();
}
}
}, 50);
}
public void setOnScrollStoppedListener(OnScrollStopListener yListener) {
listener = yListener;
}
}
답변
이 질문에 대한 나의 접근 방식은 타이머를 사용하여 다음 2 개의 “이벤트”를 확인하는 것입니다.
1) onScrollChanged () 호출 중지
2) 스크롤 뷰에서 사용자의 손가락을 뗀 경우
public class CustomScrollView extends HorizontalScrollView {
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
Timer ntimer = new Timer();
MotionEvent event;
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
checkAgain();
super.onScrollChanged(l, t, oldl, oldt);
}
public void checkAgain(){
try{
ntimer.cancel();
ntimer.purge();
}
catch(Exception e){}
ntimer = new Timer();
ntimer.schedule(new TimerTask() {
@Override
public void run() {
if(event.getAction() == MotionEvent.ACTION_UP){
// ScrollView Stopped Scrolling and Finger is not on the ScrollView
}
else{
// ScrollView Stopped Scrolling But Finger is still on the ScrollView
checkAgain();
}
}
},100);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
this.event = event;
return super.onTouchEvent(event);
}
}
답변
내 솔루션은 Lin Yu Cheng의 훌륭한 솔루션의 변형이며 스크롤이 시작되고 중지 된시기도 감지합니다.
1 단계. HorizontalScrollView 및 OnScrollChangedListener를 정의합니다.
CustomHorizontalScrollView scrollView = (CustomHorizontalScrollView) findViewById(R.id.horizontalScrollView);
horizontalScrollListener = new CustomHorizontalScrollView.OnScrollChangedListener() {
@Override
public void onScrollStart() {
// Scrolling has started. Insert your code here...
}
@Override
public void onScrollEnd() {
// Scrolling has stopped. Insert your code here...
}
};
scrollView.setOnScrollChangedListener(horizontalScrollListener);
2 단계. CustomHorizontalScrollView 클래스를 추가합니다.
public class CustomHorizontalScrollView extends HorizontalScrollView {
public interface OnScrollChangedListener {
// Developer must implement these methods.
void onScrollStart();
void onScrollEnd();
}
private long lastScrollUpdate = -1;
private int scrollTaskInterval = 100;
private Runnable mScrollingRunnable;
public OnScrollChangedListener mOnScrollListener;
public CustomHorizontalScrollView(Context context) {
this(context, null, 0);
init(context);
}
public CustomHorizontalScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init(context);
}
public CustomHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
// Check for scrolling every scrollTaskInterval milliseconds
mScrollingRunnable = new Runnable() {
public void run() {
if ((System.currentTimeMillis() - lastScrollUpdate) > scrollTaskInterval) {
// Scrolling has stopped.
lastScrollUpdate = -1;
//CustomHorizontalScrollView.this.onScrollEnd();
mOnScrollListener.onScrollEnd();
} else {
// Still scrolling - Check again in scrollTaskInterval milliseconds...
postDelayed(this, scrollTaskInterval);
}
}
};
}
public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) {
this.mOnScrollListener = onScrollChangedListener;
}
public void setScrollTaskInterval(int scrollTaskInterval) {
this.scrollTaskInterval = scrollTaskInterval;
}
//void onScrollStart() {
// System.out.println("Scroll started...");
//}
//void onScrollEnd() {
// System.out.println("Scroll ended...");
//}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mOnScrollListener != null) {
if (lastScrollUpdate == -1) {
//CustomHorizontalScrollView.this.onScrollStart();
mOnScrollListener.onScrollStart();
postDelayed(mScrollingRunnable, scrollTaskInterval);
}
lastScrollUpdate = System.currentTimeMillis();
}
}
}