0

모두 환영합니다.ListView가있는 FragmentStatePagerAdapter ... 스크롤없이 목록 뷰의 내용을 업데이트하는 방법은 무엇입니까?

각 페이지에 Listview가있는 FragmentStatePagerAdapter가 있습니다. FragmentStatePagerAdapter는 매초마다 일부 페이지에 대한 새 데이터로 새로 고침됩니다 (각 페이지가 목록보기임을 기억하십시오).

StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, ((MainActivity) getActivity()).getData(mPage)); 
listView.setAdapter(adapter); 

문제는, 그래서 모든 초 listView.setAdapter(adapter);가 호출 될 때, 일부 페이지 콘텐츠 및 수직 스크롤을 많이 가지고있는의 스크롤입니다 :

내가는, setAdapter와 목록보기의 내용을 설정 listview는 그의 위대함을 강요 당한다. 그것은 매우 비정상적이고 성가신 행동입니다.

I에 유래에이를 찾을 수 : notifyDataSetChanged() makes the list refresh and scroll jumps back to the top

문제는 이러한 솔루션은 일반 ViewPager 또는 일반 어댑터 ​​위해 설계하고 시도 후 난 여전히 같은 문제를 가지고 있기 때문에, FragmentStatePageAdapter 또는 뭔가 작동하지 않을 수 있다는 것입니다 .

public class CollectionPagerAdapter extends FragmentStatePagerAdapter { 
    public CollectionPagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public Fragment getItem(int position) { 
     return ObjectFragment.newInstance(position); 
    } 

    @Override 
    public int getCount() { 
     return infoTitlesArray.length; 
    } 

    @Override 
    public CharSequence getPageTitle(int position) { 
     return infoTitlesArray[position]; 
    } 
} 

문제 갖는다 ViewPager : 조각의

<android.support.v4.view.ViewPager 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     android:id="@+id/pager" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"> 

     <android.support.v4.view.PagerTitleStrip 
      android:id="@+id/pager_title_strip" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_gravity="top" 
      android:paddingTop="4dp" 
      android:paddingBottom="4dp" 
      style="@style/CustomPagerTitleStrip"/> 
    </android.support.v4.view.ViewPager> 

레이아웃 :

은 FragmentStatePagerAdapter 내 어댑터입니다

<ListView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/listview" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"> 
</ListView> 

의 자바 코드 단편 :

public static class ObjectFragment extends Fragment implements DataUpdateListener { 
    private int mPage; 
    private ListView listView; 

    public static ObjectFragment newInstance(int page) { 
     ObjectFragment objectFragment = new ObjectFragment(); 
     Bundle args = new Bundle(); 
     args.putInt("page", page); 
     objectFragment.setArguments(args); 
     return objectFragment; 
    } 

    @Override 
    public void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     mPage = getArguments().getInt("page"); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false); 
     ((MainActivity) getActivity()).addDataUpdateListener(this); 
     listView = (ListView) rootView; 

     StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, ((MainActivity) getActivity()).getData(mPage)); 
     listView.setAdapter(adapter); 

     return rootView; 
    } 

    @Override 
    public void onDestroyView() { 
     ((MainActivity) getActivity()).removeDataUpdateListener(this); 
     super.onDestroyView(); 
    } 

    //DataUpdateListener interface methods 
    @Override 
    public int getPage() { 
     return mPage; 
    } 

    @Override 
    public void onDataUpdated(ArrayList<String> data) { 
     StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, data); 
     listView.setAdapter(adapter); 
    } 
} 

(listview 인) 내용을 업데이트해야하는 경우 onDataUpdated 메소드가 매개 변수에 수신 된 새 컨텐츠와 함께 호출됩니다. 하려고

새 어댑터 :

이 Budius에서 코드 샘플을 사용하여 충돌을 발생 어댑터를 추가 :

private static class StableArrayAdapter extends ArrayAdapter<String> { 
     HashMap<String, Integer> mIdMap = new HashMap<String, Integer>(); 

     public StableArrayAdapter(Context context, int textViewResourceId, List<String> objects) { 
      super(context, textViewResourceId, objects); 
      for (int i = 0; i < objects.size(); ++i) { 
       mIdMap.put(objects.get(i), i); 
      } 
     } 

     public void setData(List<String> objects){ 
      mIdMap.clear(); 
      for (int i = 0; i < objects.size(); ++i) { 
       mIdMap.put(objects.get(i), i); 
      } 
     } 

     @Override 
     public long getItemId(int position) { 
      String item = getItem(position); 
      return mIdMap.get(item); 
     } 

     @Override 
     public boolean hasStableIds() { 
      return true; 
     } 
    } 

편집 2 도움

편집에 대한

감사합니다 Budius 솔루션을 사용하십시오. 그러나 그것은 효과가 없습니다. 콘텐츠가 새로 고쳐지지 않습니다.

private static class StableArrayAdapter extends ArrayAdapter<String> { 
    List<String> objects; 

    public StableArrayAdapter(Context context, int textViewResourceId, List<String> objects) { 
     super(context, textViewResourceId, objects); 
     this.objects = new ArrayList<>(); 
     this.objects.addAll(objects); 
    } 

    public void setData(List<String> objects){ 
     this.objects.clear(); 
     this.objects.addAll(objects); 
    } 

    @Override 
    public long getItemId(int position) { 
     return Math.abs(objects.get(position).hashCode()); 
    } 

    @Override 
    public boolean hasStableIds() { 
     return true; 
    } 
} 

답변

1

setAdapter(adapter)를 호출하고 정상으로 돌아 목록은 정상과 "기대"동작입니다. Afterall은 새로운 어댑터이고 프레임 워크는 새로운 어댑터가 이전 어댑터와 어떤 관계가 있다고 가정해서는 안됩니다.

해결책은 여전히 ​​notifyDataSetChanged()이고 적절한 사용은 stableIds이어야합니다. 이를 염두에두고, 새로 작성하는 대신 ArrayList<String> data을 전환하기 만하면됩니다.이런 식으로 뭔가 :

@Override 
public void onDataUpdated(ArrayList<String> data) { 
    adapter.setData(data); // internally replaces the old data with this new one 
    adapter.notifyDataSetChanged(); 
} 

다음 StableArrayAdapter의 구현에 당신이 오버라이드 (override) 할 필요가 hasStableIds는 true를 반환하고 meaningfull 값을 반환 getItemId를 오버라이드 (override)합니다. 데이터가 단순한 문자열이라고 생각하면 약간 까다 롭습니다. 그러나 Math.abs(value.hashCode)과 같은 것으로 충분할 것입니다.

추신 : ListView 대신 RecyclerView을 사용하는 것이 좋습니다. 개인적으로 나는 왜 구글이 ListView를 더 이상 사용하지 않는지 이해하지 못한다. 당신이 ID 당신이 가지고있는 데이터에 관련된 다이렉트 시스템을 이야기하고 당신이 ID를 돌려 그와

public class StableArrayAdapter extends /* whatever you're extending */ { 

    ... your code as usual 

    @Override public boolean hasStableIds() { return true; } 
    @Override public long getItemId(int position) { 
     return Math.abs(data.get(position).hashCode()); 
    } 
} 

:

편집 :이 같은 sometihng 의견에 따라

, 연결되어있는 경우에도 데이터에 연결됩니다. 의미, 그것은 하나의 데이터에 대한 하나의 ID와 다른 데이터에 대한 다른 ID가 될 것입니다.

그런 식으로 ListView에 알려 주면 ID를 기반으로 위치를 유지할 수 있습니다.

편집 2 :

나는 당신의 오류를 발견했습니다. 어댑터는 ArrayAdapter에서 확장됩니다. 이미 내부적으로는 List<String>이고, getItem(position);으로 전화하면 데이터는 내부 목록에서 가져옵니다.

수정 사항은 간단합니다.

  • List<String> objects;을 삭제하고 내부 목록을 사용하십시오. 같은

    뭔가 :

당신이 ArrayAdapter와 (https://developer.android.com/reference/android/widget/ArrayAdapter.html)에서 add, addAllclear 방법 호출 데이터를 업데이트하려면

@Override 
public void onDataUpdated(ArrayList<String> data) { 
    adapter.clear(); 
    adapter.addAll(data); 
    adapter.notifyDataSetChanged(); 
} 
+0

안녕하세요, 답변 주셔서 감사합니다. 제발, 당신이 나에게 약간의 코드 예제 나 설명을 줄 수 있습니까? StableArrayAdapter 구현시 hasStableIds를 재정의하여 true를 반환하고 getItemId를 재정의하여 의미있는 값을 반환해야합니다. 데이터가 단순한 문자열이라는 점을 감안할 때 약간 힘들지만 Math.abs (value.hashCode)와 같은 것으로 충분해야합니다. " – NullPointerException

+0

. 내 편집을 참조하십시오. – Budius

+0

mmmm, java.lang.NullPointerException가 발생했습니다 : null 객체 참조에서 가상 메소드 'int java.lang.Integer.intValue()'을 호출하려고 시도했습니다. at com.myapp.activities.MainActivity $ StableArrayAdapter.getItemId – NullPointerException