많은 구성 요소가있는 ‘페이지’가 있으며 콘텐츠가 장치 높이보다 더 깁니다. ScrollView
좋습니다. 모든 레이아웃 (전체 페이지)을 , 문제 없습니다.
구성 요소 중 하나는 ViewPager
. 이것은 올바르게 렌더링되지만 스 와이프 / 플링에 대한 응답이 올바르게 수행되지 않고 불안정하고 항상 작동하지는 않습니다. 와 ‘혼란스러워’하는 것 같아서 ScrollView
정확한 수평선으로 날아갈 때만 100 % 작동합니다.
를 제거하면 ScrollView
ViewPager가 완벽하게 응답합니다.
주변을 검색했지만 알려진 결함으로 발견되지 않았습니다. 다른 사람이 이것을 경험 한 적이 있습니까?
- 플랫폼 버전 : 1.6
- 호환성 라이브러리 v4.
- 장치 : HTC Incredible S
다음은 테스트 할 수있는 몇 가지 예제 코드입니다 ScrollView
. 올바르게 작동하는지 확인하기 위해 주석 처리 하십시오.
활동:
package com.ss.activities;
import com.ss.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.widget.TextView;
public class PagerInsideScollViewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
vp.setAdapter(new MyPagerAdapter(this));
}
}
class MyPagerAdapter extends PagerAdapter {
private Context ctx;
public MyPagerAdapter(Context context) {
ctx = context;
}
@Override
public int getCount() {
return 2;
}
@Override
public Object instantiateItem(View collection, int position) {
TextView tv = new TextView(ctx);
tv.setTextSize(50);
tv.setTextColor(Color.WHITE);
tv.setText("SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
"SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
"SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, SMILE DUDE, " +
"SMILE DUDE, SMILE DUDE, SMILE DUDE");
((ViewPager) collection).addView(tv);
return tv;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
형세:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="300dp" />
</LinearLayout>
</ScrollView>
답변
나는 같은 문제가 있었다. 내 해결책은 requestDisallowInterceptTouchEvent
ViewPager 스크롤이 시작될 때 호출 하는 것입니다.
내 코드는 다음과 같습니다.
pager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
pager.setOnPageChangeListener(new SimpleOnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
pager.getParent().requestDisallowInterceptTouchEvent(true);
}
});
답변
답변
해결책은 다음과 같습니다.
mPager.setOnTouchListener(new View.OnTouchListener() {
int dragthreshold = 30;
int downX;
int downY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getRawX();
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
if (distanceY > distanceX && distanceY > dragthreshold) {
mPager.getParent().requestDisallowInterceptTouchEvent(false);
mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
} else if (distanceX > distanceY && distanceX > dragthreshold) {
mPager.getParent().requestDisallowInterceptTouchEvent(true);
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
mPager.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
});
기본적으로 누를 때 X, Y 값을 설정하고 드래그하는 동안 거리를 계산하여 가고 싶은 방향을 결정합니다. 귀하의 경우에 최적화하기 위해 dragthreshold를 가지고 놀아보십시오.
답변
ViewPager를 사용하면 페이지 변경 이벤트를 캡처하고 ScrollView가 페이지 변경을 일으킨 터치 이벤트를 가로채는 것을 방지 할 수 있습니다.
이것은 ViewGroup.requestDisallowInterceptTouchEvent(boolean)
. 또한 사용자가 ViewPager에서 드래그를 시작하더라도 ScrollView를 드래그 할 수 있지만 페이저에서 수평 드래그는 ScrollView 간섭없이 계속 작동합니다.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Add android:id for your ScrollView in your layout
final ScrollView sv = (ScrollView) findViewById(R.id.scrollview);
final ViewPager vp = (ViewPager) findViewById(R.id.viewpager);
vp.setAdapter(new MyPagerAdapter(this));
// Use a page-change listener to respond to begin-drag events:
vp.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int newState) {
if (newState == ViewPager.SCROLL_STATE_DRAGGING) {
// Prevent the ScrollView from intercepting this event now that the page is changing.
// When this drag ends, the ScrollView will start accepting touch events again.
sv.requestDisallowInterceptTouchEvent(true);
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
이것은 Android 2.3.4 및 4.2.1의 Android 지원 v4 라이브러리에서 저에게 적합합니다.
답변
@Michael Herbig의 솔루션을 적용했습니다. 이것의 장점 setOnTouchListener
은 ViewPager (예 : ViewPagerIndicator)뿐만 아니라 자체 포함 된 클래스가 아닌 모든 뷰에서 작동한다는 것 입니다.
사용 예 :
// runStatsPager is a android.support.v4.view.ViewPager;
runStatsPager.setOnTouchListener(new ViewInScrollViewTouchHelper(runStatsPager));
// runStatsPagerIndicator is a com.viewpagerindicator.TitlePageIndicator
runStatsPagerIndicator.setOnTouchListener(new ViewInScrollViewTouchHelper(runStatsPagerIndicator));
그리고 수업 :
class ViewInScrollViewTouchHelper implements View.OnTouchListener {
private final ScrollView scrollView;
private final View viewInScrollView;
int dragthreshold = 30;
int downX;
int downY;
public ViewInScrollViewTouchHelper(View viewInScrollView) {
if (viewInScrollView == null) {
throw new IllegalArgumentException("viewInScrollView cannot be null.");
}
ViewParent parent = viewInScrollView.getParent();
ScrollView scrollView = null;
do {
if (parent instanceof ScrollView) {
scrollView = (ScrollView) parent;
break;
}
} while(parent != null && (parent = parent.getParent()) != null);
if (scrollView == null) {
throw new IllegalArgumentException("View does not have a ScrollView in its parent hierarchy.");
}
this.scrollView = scrollView;
this.viewInScrollView = viewInScrollView;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getRawX();
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
if (distanceY > distanceX && distanceY > dragthreshold) {
viewInScrollView.getParent().requestDisallowInterceptTouchEvent(false);
scrollView.getParent().requestDisallowInterceptTouchEvent(true);
} else if (distanceX > distanceY && distanceX > dragthreshold) {
viewInScrollView.getParent().requestDisallowInterceptTouchEvent(true);
scrollView.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
scrollView.getParent().requestDisallowInterceptTouchEvent(false);
viewInScrollView.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
}
답변
public class WrapContentHeightViewPager extends ViewPager {
public WrapContentHeightViewPager(Context context) {
super(context);
}
public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override public boolean onTouch (View v, MotionEvent event) {
int dragthreshold = 30;
int downX = 0;
int downY = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getRawX();
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
if (distanceY > distanceX && distanceY > dragthreshold) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(false);
mScrollView.getParent().requestDisallowInterceptTouchEvent(true);
} else if (distanceX > distanceY && distanceX > dragthreshold) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
}
break;
case MotionEvent.ACTION_UP:
mScrollView.getParent().requestDisallowInterceptTouchEvent(false);
mViewPager.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return false;
}
두 개 이상을 사용하면 매우 잘 작동합니다. 내 코드에서도 사용했습니다.
답변
따라서이 접근 방식을 사용 하면 (부모)가 이벤트를 훔치고 현재 스크롤을 취소하는 것에 대해 걱정할 필요없이 ViewPager
스크롤을 X
방향으로 ScrollView
두었습니다. 더 중요한 것은이 방향으로 ViewPager
스크롤 할 수 있는가있는 영역도 남긴다는 것입니다 Y
.
public class CustomViewPager extends ViewPager {
private GestureDetector mGestureDetector;
View.OnTouchListener mGestureListener;
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new Detector());
}
@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
mGestureDetector.onTouchEvent(motionEvent);
return super.onTouchEvent(motionEvent);
}
class Detector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
requestDisallowInterceptTouchEvent(true);
return false;
}
}
}