5

요약 : 사용자 지정 어댑터 래퍼를 통해 ListView에 제목 열을 동적으로 추가하려고합니다. ListView가 스크롤 위치를 동기화 상태로 유지하는 데 문제가 있습니다. 실행 가능한 데모 프로젝트가 제공됩니다.동적 어댑터 래퍼에서 notifyDataSetChanged/ListView 문제를 해결하는 방법 Android

사용자가 현재보고있는 것보다 몇 자리 앞의 CursorAdapter의 값을 기반으로 목록에 항목을 동적으로 추가하고 싶습니다. 이렇게하려면 CursorAdapter를 래핑하고 새로운 콘텐츠를 SparseArray에 계속 유지하는 어댑터가 있어야합니다. ListView는 항목이 사용자 정의 어댑터에 추가 될 때 업데이트해야하지만 작동하도록 노력하는 많은 함정을 만났고 일부 조언을 좋아합니다.

데모 프로젝트

여기에서 다운로드 할 수 있습니다 데모에서

DynamicSectionedList.zip은, 제목은 목록 항목은 다음 문자로 전환 정확한 위치를 찾기 위해 앞으로 10 곳을 찾고 동적으로 추가됩니다. notifyDataSetChanged 각 구현은 설명 된 문제를 가진다 : 1 이 데모 notifyDataSetChanged의 중요성을 보여준다

데모(). 아무것도 클릭하면 응용 프로그램이 중단됩니다. 이것은 ListView ...에서 몇 가지 온 전성 검사로 인한 것입니다. 도덕적으로, 우리는 데이터가 변경되었음을 목록에 알릴 필요가 있습니다.

데모 2 자연스러운 다음 단계는 변경 사항이 발생할 때 ListView에 변경 사항을 알리는 것입니다. 불행히도, ListView가 앱이 터치 모드를 벗어날 때까지 모든 터치 상호 작용을 단단히 스크롤하는 동안 그렇게합니다. 이를 알기 위해서는 새로운 표제를 생성하기에 충분히 멀리 "뛰어 넘는 스크롤"이 필요합니다. 화면을 가볍게 두드리면 스크롤이 멈추지 않으며 일단 멈 추면 목록 항목 중 아무 것도 클릭 할 수 없습니다. 이는 AbsListView.onTouchEvent()의 코드가 if (!mDataChanged) { /* do very important stuff */ }이기 때문입니다.

데모 3 는이 문제를 해결하려면, 데모 3은 pendingChanges 플래그와이 변화를 "안전한"상태에 들어간 후에는 ListView를 호출 할 수있는 사용자 정의 어댑터 이익 notifyDataSetChangedIfNeeded()를 소개합니다. 변경 사항을 통지해야하는 첫 번째 요점은 ListView.layoutChildren()에 있습니다. 따라서 필요한 경우 변경 사항을 먼저 알리고 그 후 호출하여 해당 메서드를 무시합니다. 하나 이상의 표제를 지나치면서 목록 항목을 클릭하십시오.

완전히 올바르게 작동하지 않지만 제대로 작동하지 않습니다. 키보드/트랙볼을 사용하여 항목을 누르거나 선택하면 이전 위치를 올바르게 동기화하지 않고 목록을 새로 고칩니다. 허용되지 않는 목록의 맨 위로 스크롤합니다.

데모 3의 스크롤 문제는 적어도 터치 모드 정복 할 수 4 데모. 터치 다운시 notifyDataSetChangedIfNeeded()에 대한 호출을 추가하면 모든 터치 상호 작용이 예상대로 작동하고 목록 위치가 제대로 동기화 될 때 데이터 변경이 발생합니다.

그러나 장치가 터치 모드가 아닌데도 확실히 해킹과 같은 것으로 보이는 사실은 말할 것도 없습니다. 목록은 거의 항상 맨 위로 스크롤되며, 때때로 올바른 위치를 유지하는 원인을 찾을 수 없습니다.

안드로이드가 각 단계에서 나와 싸우고 있기 때문에 더 나은 접근 방식이 있어야한다고 생각합니다. 데모를 시험해보십시오. 수정 사항을 적용하면 효과적 일 수 있습니다.

이 문제를 조사 할 수있는 많은 사람들에게 감사 드리며, 코드를 작동 시키면 머리글이있는 목록에 대해 동일한 최적화를 수행하는 데 유용 할 것입니다.

+1

'LinearLayout buttons'바로 아래에있는 for 루프는 유효한 Java 구문을 갖고 있지 않습니다. 또한 목록을 채우는 코드가 누락 된 것처럼 보입니다. 이 자료를 포함하는 전체 프로젝트를 만들고 ZIP을 작성한 다음 질문에 연결하십시오. 그때조차도, 나는 당신이 많은 도움을 얻는 것에 엄청난 확률을 두지 않을 것입니다. 이것은 이것이 해독하기 어려운 코드의 상당한 덩어리이기 때문에, 당신은 당신의 질문에 정말로 어떤 질문도하지 않습니다. – CommonsWare

+1

두 번째 질문을 시작하여 실제 문제 ("목록에있는 데이터의 양이 매우 클 수 있으며 표제의 계산이 최적화가 필요할 정도로 비싸다고 생각할 수 있습니다")로 확장하는 것이 좋습니다. 여기에있는 것은 실제 문제를 해결하려고 시도하는 실제 코드와 다소 관련이 있습니다. 그러나 "표제 계산"이 "비용이 많이 드는"이유와 해결 방법을 파악하면서 실제 문제 자체에 대한 도움을 얻는 것이 더 간단 할 수 있습니다. 물론, 목요일에 휴가 주말 전에 이걸 묻는 중입니다 ... – CommonsWare

+2

BTW, FWIW, 'CursorAdapter'에 제목을 추가하는 것에 대한 제 생각으로 블로그 게시물을 작성했습니다 : http://commonsware.com/ blog/2010/12/30/adding-headings-cursoradapter.html – CommonsWare

답변

4

위에 제시된 접근법의 주된 문제점은 데이터 세트가 수퍼 클래스에 의해 실행되는 코드 내에서 직접 변경되고 있다는 점입니다. getView()에서 표제를 채우는 작업의 중간 또는 슈퍼 클래스의 "안전하지 않은"상태 일 수있는 데이터를 변경했습니다. 따라서 fling 스크롤 중에 onNotifyDataSetChanged가 호출 될 때 모든 터치 상호 작용이 중단 된 것입니다. 데이터는 어댑터 메소드가 아닌 외부 소스에서 추가해야합니다.

핸들러 또는 AsyncTask를 사용하여 제목을 어댑터에 추가하고 notifyDataSetChanged를 호출하여이를 수행 할 수 있습니다. 이들은 UI 스레드에서 실행되며 수퍼 클래스 상태를 방해하지 않습니다. AsyncTask 개념을 도입하여 목록에 데이터를 추가하는 CommonsWare의 EndlessAdapter 예제 덕택입니다.

변경 한 후에는 onTouchEvent() 및 layoutChildren() 해킹이 더 이상 필요하지 않습니다.

관련 문제