所以,我有这个ScrollView
和一个孩子 - 一个有两个孩子的LinearLayout
:一个TextView
和一个ViewPager
。 ViewPager
包含许多元素的布局,这就是为什么我需要垂直滚动的能力。只有ViewPager
中的页面可以水平滚动(即:我只想在ViewPager
内水平滑动)。那一个TextView
不能水平滚动,但应该与我的ViewPager
滚动。
简单?没有。
我在StackOverflow上看到了非常相似的问题(here,here和here以及here)。所有建议的解决方案都不适合我:(
我看到的是this
在ViewPager中嵌入ScrollView不是一个选项 - UI的设计禁止这样做。
也许这是我用编程方式填充视图寻呼机中的每个页面的东西?嗯...
任何建议,将不胜感激。
我的代码:
activity_main.xml中:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:fillViewport="true" >
<LinearLayout
android:id="@+id/linearLayoutGeneral"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tvText"
android:layout_width="fill_parent"
android:layout_height="200dp"
android:text="Test text" />
<android.support.v4.view.ViewPager
android:id="@+android:id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</android.support.v4.view.ViewPager>
</LinearLayout>
</ScrollView>
ViewPager中的每个页面都有这样的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/layoutData"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</LinearLayout>
单个元素在这样的页面中的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:gravity="center"
android:orientation="vertical"
android:background="@android:color/white" >
<TextView
android:id="@+id/textView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Large Text"
android:background="@android:color/black"
android:textColor="@android:color/white"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
单页片段也很简单:
public class DayFragment extends Fragment {
private static final String TAG = DayFragment.class.getSimpleName();
public String tag;
LinearLayout data;
View mView;
final int ROWS_NUM = 60;
public DayFragment() {
}
/**
* (non-Javadoc)
*
* @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
* android.view.ViewGroup, android.os.Bundle)
*/
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
mView = (LinearLayout) inflater.inflate(R.layout.day, container, false);
setUpControls();
generateData();
String text = getArguments().getString("text");
Log.d(TAG, "creating view with text: " + text);
return mView;
}
private void setUpControls() {
data = (LinearLayout) mView.findViewById(R.id.layoutData);
}
private void generateData() {
for (int i = 0; i < ROWS_NUM; i++) {
View v = createRow(i);
data.addView(v);
}
}
private View createRow(int num) {
LayoutInflater inflater = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.row, null);
TextView tv = (TextView) v.findViewById(R.id.textView);
tv.setText("Data nr: " + num);
return v;
}
public static DayFragment newInstance(String text) {
Log.d(TAG, "newInstance with text: " + text);
DayFragment f = new DayFragment();
f.tag = text;
// Supply text input as an argument.
Bundle args = new Bundle();
args.putString("text", text);
f.setArguments(args);
return f;
}
}
我有问题,ViewPager
表现得很奇怪,我发现原因是因为有时ScrollView
重新获得焦点,然后ViewPager
失去焦点。如果你在ViewPager
中有一个ScrollView
并且你希望它总是在你触摸时保持焦点并且ScrollView
从未得到焦点,那么设置requestDisallowInterceptTouchEvent
就是这样。这有帮助吗?
mViewPager= (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(adapter);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
延伸到Marius的答案,允许父母垂直滚动:
我注意到当你在初始滚动时调用'requestDisallowInterceptTouchEvent'方法时,它会在视图寻呼机顶部垂直滚动时阻止在父视图上垂直滚动。
我的解决方案是在用户开始水平滚动指定距离('margin'变量)后才触发该方法。
mViewPager.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
// How far the user has to scroll before it locks the parent vertical scrolling.
final int margin = 10;
final int fragmentOffset = v.getScrollX() % v.getWidth();
if (fragmentOffset > margin && fragmentOffset < v.getWidth() - margin) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
我有同样的问题尝试我的代码,你我们应该禁用垂直滚动并将事件发送到Scrollview:
private int dragThreshold = 10, int downX = 0, int downY = 0;
vPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP){
downX = (int) event.getRawX();
downY = (int) event.getRawY();
return false;
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
int distanceX = Math.abs((int) event.getRawX() - downX);
int distanceY = Math.abs((int) event.getRawY() - downY);
ExceptionHelpers.dLog("OnTouchListener", "distance X : "+distanceX+" , distance Y : "+distanceY);
if(distanceY > distanceX && distanceY > dragThreshold){
v.getParent().requestDisallowInterceptTouchEvent(false);
return true;
}
}
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
您还可以通过添加设置最小水平滚动:
else {
distanceX > distanceY && distanceX > dragThreshold)
}
请记住,建筑工人做 - >尝试尝试和尝试
开发人员(我们)做 - >思考,思考和尝试
好锁
试试这个
public static class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) < Math.abs(distanceX);
}
}
并为viewpager
final GestureDetector mGestureDetector= new GestureDetector(this,new XScrollDetector());
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScrollView.requestDisallowInterceptTouchEvent(mGestureDetector.onTouchEvent(event));
return false;
}
});
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;
}
});
我尝试了很多东西,但有些东西对我不起作用,有些东西太复杂,代码很脏。
我用这种方式解决了这个ViewPager in ScrollView
问题。
您可以在ViewPager.onMeasure()
或ViewPager.OnPageChangeListener()
中更新viewPager大小
当ui在片段中发生变化时,每次都会调用onMeasure()
方法。但我有很多不同的ui(例如TabButton),我只在用户滚动页面时更新了视图寻呼机大小。
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// will work when ViewPager shows first fragment.
if (position != currentPosition) {
currentPosition = position;
updateViewPagerSize(position);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
根据具体情况,您可能需要重点关注ScrollView的工作原理。
public void updateViewPagerSize(int position) {
View view = mPager.getChildAt(position);
view.measure(ViewPager.LayoutParams.WRAP_CONTENT, ViewPager.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, view.getMeasuredHeight());
mPager.setLayoutParams(params);
edtText.requestFocus(); //you might have to give a focus something to use scroll.
}
一个Activity中的水平和垂直滚动通常是个问题。 ViewPager可以很好地处理这个问题。我不知道你的问题是否有一个好的解决方案。但我建议摆脱主要布局中的ScrollView和TextView。
使用ViewPager作为布局的起点,并将垂直ScrollView放在ViewPager碎片中。
禁止ScrollView拦截触摸事件。这要求用户在ViewPager外部触摸以向上或向下滚动。
mScrollView = (ScrollView) findViewById(R.id.scroll_view);
mViewPager= (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(adapter);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mScrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
});