web-dev-qa-db-ja.com

ScrollView内のViewPager-垂直スクロールが機能しない

だから、私はこれを1つの子を持つScrollView-LinearLayoutTextViewの2つの子を持つViewPagerを持っています。 ViewPagerには多くの要素を含むレイアウトが含まれているため、垂直方向にスクロールする機能が必要です。 ViewPagerのページのみを水平方向にスクロールできます(つまり、ViewPager内でのみ水平方向にスワイプしたい)。そのTextViewは水平方向にスクロールする必要はありませんが、私のViewPagerと一緒にスクロールする必要があります。

簡単?番号。

StackOverflowで非常によく似た問題が発生しました( herehere および here および here ))。提案された解決策のどれも私のために機能しません:(

私が見るのは this <-私の甘いUI :)ですが、垂直方向にスクロールできません:(

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;
    }

}
16
Genkaku

ViewPagerの動作がおかしいという問題がありました。その理由は、ScrollViewが再びフォーカスを取得し、ViewPagerがフォーカスを失う場合があるためです。 ViewPagerScrollViewがあり、それに触れたときに常に焦点を合わせておき、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;
        }
    });
29

マリウスの答えに拡張して、親の垂直スクロールを許可します。

最初のスクロールで「requestDisallowInterceptTouchEvent」メソッドを呼び出すと、ビューページャーの上で垂直にスクロールすると、親ビューで垂直スクロールがブロックされることに気付きました。

私の解決策は、ユーザーが指定された距離(「マージン」変数)だけ水平方向にスクロールを開始した後にのみ、そのメソッドをトリガーすることでした。

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;
    }
});
11
elimirks

これを試して

    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;

        }
    });
1
user3024215
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;
    }
});
1

同じ問題でコードを試してみましたが、vertical scrollを無効にし、eventを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)
}

そして、覚えておいてください、建設労働者は->試してみて試してみてください

開発者(私たち)が行う->考え、考え、試す

良いロック

1
AndroSco

多くのことを試しましたが、うまくいかなかったり、複雑すぎたり、コードが汚いものもあります。

私はこの_ViewPager in ScrollView_の問題をこの方法で解決しました。
viewPagerのサイズはViewPager.onMeasure()またはViewPager.OnPageChangeListener()で更新できます

メソッドonMeasure()は、UIがフラグメントで変更されるたびに呼び出されます。しかし、さまざまな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.
}
_
0
youngwoon