Android - ScrollView in ScrollView
안드로이드 개발을 하다보면 UI/UX 구현에서 문제가 발생하는 경우가 종종 있습니다. 이번 블로그에서는 스크롤뷰를 중첩해서 사용할 때 발생하는 문제와 문제 해결법에 대해 알아보죠.
유저가 스크롤할 수 있는 인터페이스를 제공함으로써 화면보다 큰 컨텐츠를 표시할 때 쓰이는 스크롤뷰는 터치이벤트를 사용하여 스크롤 여부를 결정합니다. 문제는 다음과 같은 경우에 발생합니다.
<!-- 예제에 사용되는 레이아웃입니다 : R.layout.example --> <ScrollView android:id="parent" > <!-- Parent ScrollView --> <LinearLayout> <!-- container --> <ScrollView android:id="child" > <!-- Child ScrollView --> <LinearLayout> <!-- views --> </LinearLayout> </ScrollView> </LinearLayout> </ScrollView>
위에서 보이는 것과 같이 두개의 중첩된 스크롤뷰 parent와 child는 같은 영역에 표시되기 때문에 서로 터치이벤트를 공유하게 됩니다. 문제는 유저가 child를 스크롤 했을때 parent가 유저의 터치이벤트를 가로채(intercept) 처리하고 이벤트를 종료하는데 있습니다.
대부분의 UI/UX 기획은 유저가 터치한 영역을 스크롤 하게끔 되어 있습니다. 하지만 안드로이드 위젯의 특성상 터치이벤트를 처리하는 부모 위젯이 존재할 경우 자식 위젯의 터치이벤트를 부모 위젯이 intercept하여 처리하게끔 되어 있어 기획과 다른 UX가 나오게 됩니다.
이런 경우에 대비하여 안드로이드는 child가 parent에게 터치이벤트를 가로채지 말 것을 요청하는 메소드를 다음과 같이 android.view.ViewParent에 정의하였습니다.
/** * child가 parent를 포함한 모든 ancestor에게 터치이벤트를 가로채지 * 말 것을 요청할때 호출됩니다. * * parent는 child의 요청을 자신의 parent에게 전달할 필요가 있으며 * 터치이벤트가 지속되는 동안 (UP이나 CANCEL이 발생할 때 까지) 해당 child의 * 터치이벤트를 가로채지 않습니다. * * @param disallowIntercept True - parent가 터치이벤트를 가로채지 않습니다. */ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
요점은 child 스크롤뷰가 터치이벤트가 발생한 시점에 container에 requestDisallowInterceptTouchEvent(true)를 실행하는 것입니다. 다음과 같은 코드를 통해 해당 요청을 수행할 수 있습니다.
private ScrollView mChild; @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.example); mChild = (ScrollView) findViewById(R.id.child); mChild.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent ev) { v.getParent().requestDisallowInterceptTouchEvent(true); return false; } ); }
위 코드를 실행하면 중첩된 스크롤뷰 구조에서 유저가 터치하는 영역에 위치한 스크롤뷰가 스크롤되는 것을 확인할 수 있습니다.










