Android 개발 의 DiffUtil 사용 에 대한 자세 한 설명

10100 단어 androiddiffutil
앞 에 쓰 면...
DiffUtil 은 집합 변 화 를 찾 는 도구 류 로 RecyclerView 와 함께 사용 합 니 다.RecyclerView 에 대해 잘 모 르 면 자 료 를 읽 을 수 있 습 니 다.여 기 는 소개 하지 않 겠 습 니 다.
효과 그림 먼저 넣 기:

우리 가 버튼 을 눌 렀 을 때 이 RecyclerView 가 보 여 준 집합 이 바 뀌 었 고,어떤 요 소 는 추가 되 었 다(8.Jason),어떤 요 소 는 이동 되 었 다(3.Rose),심지어 수정 되 었 다(2.Fndroid).
RecyclerView 는 각 항목 의 애니메이션 을 다른 방식 으로 새로 고 칩 니 다.
     notifyItemInserted
     notifyItemChanged
     notifyItemMoved
     notifyItemRemoved
연속 적 인 몇 개의 Item 리 셋 에 대해 서 는 다음 과 같이 호출 할 수 있 습 니 다.
     notifyItemRangeChanged
     notifyItemRangeInserted
     notifyItemRangeRemoved
한편,집합 이 변화 할 때notifyDataSetChanged방법 으로 전체 인터페이스의 갱신 만 할 수 있 고 집합 변화 에 따라 모든 변화 요소 에 애니메이션 을 추가 할 수 없다.그래서 이 문 제 를 해결 하기 위해 DiffUtil 이 생 겼 습 니 다.
DiffUtil 의 역할 은 집합 중의 모든 Item 에 발생 하 는 변 화 를 찾아내 고 모든 변화 에 대응 하 는 갱신 을 하 는 것 이다.
이 DiffUtil 은 Eugene Myers 의 차별 화 된 알고리즘 을 사용 합 니 다.이 알고리즘 자 체 는 요소 의 이동 을 검사 할 수 없습니다.즉,이동 은 먼저 삭제 하고 다시 증가 하 는 것 으로 간주 되 고 DiffUtil 은 알고리즘 결과 후에 이동 검 사 를 한 번 합 니 다.원소 의 이동 을 감지 하지 않 은 상태 에서 알고리즘 의 시간 복잡 도 는 O(N+D2)이 고 원소 의 이동 을 감지 하면 복잡 도 는 O(N2)라 고 가정 한다.따라서 집합 자체 가 순서 가 정 해 져 있 으 면 이동 검 사 를 하지 않 고 효율 을 높 일 수 있다. 
다음은 우리 함께 이 공 구 를 어떻게 사용 하 는 지 보 자.
우선 각 Item 에 대해 데 이 터 는 Student 대상 입 니 다.

class Student {
 private String name;
 private int num;

 public Student(String name, int num) {
  this.name = name;
  this.num = num;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getNum() {
  return num;
 }

 public void setNum(int num) {
  this.num = num;
 }
}
이어서 레이아웃(생략)과 어댑터 를 정의 합 니 다.

class MyAdapter extends RecyclerView.Adapter {
  private ArrayList<Student> data;

  ArrayList<Student> getData() {
   return data;
  }

  void setData(ArrayList<Student> data) {
   this.data = new ArrayList<>(data);
  }

  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   View itemView = LayoutInflater.from(RecyclerViewActivity.this).inflate(R.layout.itemview, null);
   return new MyViewHolder(itemView);
  }

  @Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
   MyViewHolder myViewHolder = (MyViewHolder) holder;
   Student student = data.get(position);
   myViewHolder.tv.setText(student.getNum() + "." + student.getName());
  }

  @Override
  public int getItemCount() {
   return data.size();
  }

  class MyViewHolder extends RecyclerView.ViewHolder {
   TextView tv;

   MyViewHolder(View itemView) {
    super(itemView);
    tv = (TextView) itemView.findViewById(R.id.item_tv);
   }
  }
 }
데이터 집합 초기 화:

private void initData() {
  students = new ArrayList<>();
  Student s1 = new Student("John", 1);
  Student s2 = new Student("Curry", 2);
  Student s3 = new Student("Rose", 3);
  Student s4 = new Student("Dante", 4);
  Student s5 = new Student("Lunar", 5);
  students.add(s1);
  students.add(s2);
  students.add(s3);
  students.add(s4);
  students.add(s5);
 }
이어서 어댑터 를 실례 화하 고 RecyclerView 에 설정 합 니 다.

@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_recycler_view);
  initData();
  recyclerView = (RecyclerView) findViewById(R.id.rv);
  recyclerView.setLayoutManager(new LinearLayoutManager(this));
  adapter = new MyAdapter();
  adapter.setData(students);
  recyclerView.setAdapter(adapter);
 }
이 내용 들 은 모두 이 편의 내용 이 아니 지만 주의해 야 할 부분 은 Adapter 의 정의 입 니 다.

class MyAdapter extends RecyclerView.Adapter {
  private ArrayList<Student> data;

  ArrayList<Student> getData() {
   return data;
  }

  void setData(ArrayList<Student> data) {
   this.data = new ArrayList<>(data);
  }

  //       
   ...... 
 }
여기setData방법 은 Array List 의 인용 을 직접 저장 하 는 것 이 아니 라 Array List 를 다시 만 드 는 것 입 니 다.먼저 기억 하고 나중에 왜 이렇게 해 야 하 는 지 설명 합 니 다.
DiffUtil 의 사용 방법:
마 우 스 를 눌 렀 을 때 Array List 의 내용 을 수정 합 니 다.

public void change(View view) {
  students.set(1, new Student("Fndroid", 2));
  students.add(new Student("Jason", 8));
  Student s2 = students.get(2);
  students.remove(2);
  students.add(s2);

  ArrayList<Student> old_students = adapter.getData();
  DiffUtil.DiffResult result = DiffUtil.calculateDiff(new MyCallback(old_students, students), true);
  adapter.setData(students);
  result.dispatchUpdatesTo(adapter);
 }
2-6 줄 은 집합 을 수정 하고 8 줄 은 adapter 의 집합 을 오래된 데이터 로 가 져 옵 니 다.
9 번 째 줄 호출DiffUtil.calculateDiff방법 으로 집합 차 이 를 계산 하 는 데 중심 을 두 어야 합 니 다.여 기 는 CallBack 인터페이스의 실현 류(계산 규칙 을 지정 하 는 데 사용)를 전송 하고 새로운 데 이 터 를 이 인터페이스의 실현 류 에 전달 해 야 합 니 다.마지막 으로 boolean 유형의 매개 변수 가 있 습 니 다.이 매개 변 수 는 Move 검 사 를 해 야 하 는 지 여 부 를 지정 합 니 다.필요 하지 않 으 면 Item 이 이동 하면...먼저 remove 하고 insert 로 여 겨 집 니 다.트 루 로 지정 되 어 움 직 이 는 그림 의 이동 효과 가 있 습 니 다.
열 번 째 줄 은 Adapter 에 새로운 데 이 터 를 다시 설정 합 니 다.
11 번 째 줄 에서 9 번 째 줄 에서 받 은 DiffResult 대상dispatchUpdatesTo을 호출 하 는 방법 은 RecyclerView 에 변 화 된 아 이 템 을 새로 고침 하 라 고 알 립 니 다.
여기 서 위 에서 말 한setData방법 으로 돌아 갑 니 다.우 리 는 여기 서 두 개의 집합 을 구분 해 야 하기 때문에setData방법 에서 인용 을 직접 저장 하면 2-6 줄 의 수정 은 Adapter 의 집합(자바 지식)을 직접 수정 합 니 다.
아 이 템 의 이동 을 검사 하지 않 으 면 다음 과 같이 효과 가 있 습 니 다.

이어서 CallBack 인터페이스의 실현 클래스 가 어떻게 정의 되 는 지 봅 시다.

private class MyCallback extends DiffUtil.Callback {
  private ArrayList<Student> old_students, new_students;

  MyCallback(ArrayList<Student> data, ArrayList<Student> students) {
   this.old_students = data;
   this.new_students = students;
  }

  @Override
  public int getOldListSize() {
   return old_students.size();
  }

  @Override
  public int getNewListSize() {
   return new_students.size();
  }

  //   Item      
  @Override
  public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
   return old_students.get(oldItemPosition).getNum() == new_students.get(newItemPosition).getNum();
  }

  //   Item           ,  Item       
  @Override
  public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
   return old_students.get(oldItemPosition).getName().equals(new_students.get(newItemPosition).getName());
  }
 }
여 기 는 학 번 에 따라 같은 아 이 템 여 부 를 판단 하고 이름 에 따라 이 아 이 템 이 수정 되 었 는 지 여 부 를 판단 합 니 다.
실제로 이 Callback 추상 류 는 또 하나의 방법getChangePayload() 이 있다.이 방법의 역할 은 우리 가 이 방법 을 통 해 Adapter 에 게 이 Item 에 대해 전체 업데이트 가 아니 라 부분 적 인 업 데 이 트 를 할 수 있다 는 것 을 알려 주 는 것 이다.
이 페 이 로드 가 뭔 지 알 아야 돼 요?payload 는 Item 의 변 화 를 묘사 하 는 대상 입 니 다.즉,우리 의 Item 에 어떤 변화 가 생 겼 는 지,이러한 변 화 는 하나의 payload 로 봉 인 됩 니 다.그래서 우 리 는 보통 Bundle 로 충당 할 수 있 습 니 다.
이 어getChangePayload()방법 은areItemsTheSame()true 로 돌아 가 고areContentsTheSame()false 로 돌아 갈 때 리 셋 된 것 이다.즉,하나의 Item 의 내용 에 변화 가 생 겼 는데 이 변 화 는 부분 적 인 것 일 수 있다(예 를 들 어 웨 이 보 의'좋아요',우 리 는 전체 Item 이 아 닌 아이콘 만 새로 고침 해 야 한다).그래서getChangePayload()에 Object 를 패키지 하여 RecyclerView 에 부분 적 인 리 셋 을 알려 줄 수 있 습 니 다.
만약 에 상기 중학교 번호 와 이름 이 서로 다른 TextView 로 표시 된다 고 가정 하면 우리 가 한 학 번 에 해당 하 는 이름 을 수정 할 때 부분 적 으로 이름 을 바 꾸 면 된다(여기 서 예 는 비교적 불필요 해 보일 수 있 지만 하나의 Item 이 복잡 하면 쓸모 가 비교적 크다).
먼저 Callback 의 이 방법 을 다시 쓰 는 것 입 니 다.

@Nullable
  @Override
  public Object getChangePayload(int oldItemPosition, int newItemPosition) {
   Student newStudent = newStudents.get(newItemPosition);
   Bundle diffBundle = new Bundle();
   diffBundle.putString(NAME_KEY, newStudent.getName());
   return diffBundle;
  }
돌아 온 이 상 대 는 어디서 받 을 수 있 을까요?실제로RecyclerView.Adapter에서 두 가지onBindViewHolder방법 이 있 습 니 다.하 나 는 우리 가 다시 써 야 하 는 것 이 고 다른 세 번 째 매개 변 수 는 바로 payload 의 목록 입 니 다.

   @Override
   public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {}
따라서 우 리 는 Adapter 에서 이 방법 을 다시 쓰기 만 하면 됩 니 다.List 가 비어 있 으 면 원래 의 onBindViewHolder 를 실행 하여 전체 Item 의 업 데 이 트 를 진행 합 니 다.그렇지 않 으 면 paylods 의 내용 에 따라 부분 적 인 리 셋 을 진행 합 니 다.

@Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {
   if (payloads.isEmpty()) {
    onBindViewHolder(holder, position);
   } else {
    MyViewHolder myViewHolder = (MyViewHolder) holder;
    Bundle bundle = (Bundle) payloads.get(0);
    if (bundle.getString(NAME_KEY) != null) {
     myViewHolder.name.setText(bundle.getString(NAME_KEY));
     myViewHolder.name.setTextColor(Color.BLUE);
    }
   }
  }
이곳 의 pay loads 는 null 이 되 지 않 기 때문에 빈 것 인지 아 닌 지 를 직접 판단 하면 됩 니 다.

여기 주의:RecyclerView 에 대량의 데 이 터 를 불 러 오 면 알고리즘 이 바로 완성 되 지 않 을 수 있 습 니 다.ANR 의 문제 에 주의 하고 단독 스 레 드 를 열 어 계산 할 수 있 습 니 다.
총결산
Android 에서 DiffUtil 의 사용 은 여기까지 소개 되 었 습 니 다.이 글 이 Android 개발 자 들 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 면 댓 글 을 남 겨 주 십시오.

좋은 웹페이지 즐겨찾기