[Android 09] Toàn tập RecyclerView

1. RecyclerView ??? 😇 ???
RecyclerView có một vài thành phần chính:
  • Adapter: Một subclass của RecycleView.Adapter, chịu trách nhiệm cung cấp views để hiển thị các items trong tập dữ liệu (data set).
  • Position: Vị trí đã được attach vào view con, sử dụng trong phương thức getChildAt(int).
  • Binding: Quy trình chuẩn bị để hiển thị dữ liệu chính xác vào trong adapter với vị trí đã định.
  • Recycle: Một view trước đó đã hiển thị dữ liệu cho một vị trí adapter đặc biệt có thể được lưu trong cache để sử dụng lại sau này, hiển thị kiểu dữ liệu giống nhau. Nó có thể tăng rất mạnh mẽ về performance bởi đã bỏ qua việc khởi tạo layout hoặc constructor.
  • Scrap: Một view con đã được nhập vào trạng thái tạm thời trong quá trình bố trí layout. Scrap view có thể sử dụng lại mà không trở thành hoàn toàn bị tách ra khỏi RecyclerView cha, kể cả không chỉnh sửa (unmodified) nếu không cần phải binding lại hoặc modidy bởi adapter nếu view được xem xét về dữ liệu.
  • Dirty: Một view chon phải được rebound bởi adapter trước khi hiển thị.
RecylerView là một phiên bản của ListView với performance được cải thiện và thêm nhiều tính năng hơn.
2. Thêm RecyclerView vào project:
1
2
3
4
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Khởi tạo và khai báo recylerView trong class.
1
2
3
RecyclerView recyclerView = findViewById(R.id.recyclerView);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(layoutManager);
Cách thứ 2 để cài đặt layoutmanager từ xml:
1
2
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
Nếu bạn biết rằng sự thay đổi nội dung của RecylerView sẽ không thay đổi kích cỡ layout của RecylerView, sử dụng code sau để tăng performance của component. Và thêm nữa, nếu RecyclerView được set cứng kích cỡ, thì chính nó sẽ không resize bên trong các view con của nó, vì thế, nó sẽ không gọi request layout tất cả để update lại, chỉ có xử lý thay đổi chính nó mà thôi. Nếu bất cứ khi nào invalidate lại parent, thì dùng cái này:
mRecyclerView.setHasFixedSize(true);
RecylerView cung cấp trình quản lý layout để sử dụng. Vì vậy có thể khởi tạo một danh sách, 1 grid, hoặc staggered grid sử dụng RecylerView.
3. Loading items mượt mà hơn
Nếu items trong RecyclerView của bạn load dữ liệu từ network về, ví dụ hình ảnh, hoặc tiến hành các tiến trình, nó có thể mất một thời gian đáng kể và bạn có thể thấy end-up item trên màn hình nhưng nó ko được load đầy đủ. Để tránh trường hợp đó, nên sử dụng mở rộng class LinearLayoutManager để load trước một số các items trước khi chúng hiện trên màn hình.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class PreLoadingLinearLayoutManager extends LinearLayoutManager {
    private int page = 1;
    private OrientationHelper orientationHelper;

    public PreLoadingLinearLayoutManager(Context context) {
        super(context);
    }

    public PreLoadingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public PreLoadingLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void setOrientation(int orientation) {
        super.setOrientation(orientation);
        orientationHelper = null;
    }

    /**
     * Thiết đặt số trang của layout sẽ được tải trước
     *
     * @param page
     */
    public void setPage(int page) {
        this.page = page;
    }

    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        if (orientationHelper == null) {
            orientationHelper = OrientationHelper.createOrientationHelper(this, getOrientation());
        }
        return orientationHelper.getTotalSpace() * page;
    }
}
4. Sử dụng những ViewHolder khác nhau cho những ItemType khác nhau như thế nào?
Trong trường hợp bên dưới, trong một danh sách những item, có 2 loại item, 1 loại để hiển thị list entries, loại còn lại để hiển thị multiple header menu:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  View itemView = LayoutInflater.from(context).inflate(viewType, parent, false);
  return ViewHolder.create(itemView, viewType); 
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  final Item model = this.items.get(position);
    ((ViewHolder) holder).bind(model);
}
@Override
public int getItemViewType(int position) {
  return inSearchState ? R.layout.item_header : R.layout.item_entry; 
}
abstract class ViewHolder { 
  abstract void bind(Item model);
  public static ViewHolder create(View v, int viewType) {
    return viewType == R.layout.item_header ? new HeaderViewHolder(v) :new
       EntryViewHolder(v);
    }
}
static class EntryViewHolder extends ViewHolder {
   private View v;
   public EntryViewHolder(View v) { this.v = v;
}
@Override public void bind(Item model) {
  // Bind item data to entry view.
  } 
}
static class HeaderViewHolder extends ViewHolder { 
   private View v;
   public HeaderViewHolder(View v) { 
     this.v = v;
  }
@Override public void bind(Item model) { 
   //Bind item data to header view.
} }
5. Filter item bên trong Adapter của RecyclerView:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
 public void filter (String text){
        List<String> items=new ArrayList<>();
        List<String> itemsCopy=new ArrayList<>();
        if (text.isEmpty()) {
            items.clear();
            items.addAll(itemsCopy);
        } else {
            ArrayList<String> result = new ArrayList<>();
            text = text.toLowerCase();
            for (String item : itemsCopy) {
                //match by name or phone
                if (item.toLowerCase().contains(text) || item.toUpperCase().contains(text)) {
                    result.add(item);
                }
            }
            items.clear();
            items.addAll(result);
        }
    }

1
2
3
4
5
6
7
8
9
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 
@Override
  public boolean onQueryTextSubmit(String query) { adapter.filter(query);
   return true;
}
@Override
  public boolean onQueryTextChange(String newText) {
        adapter.filter(newText);
   return true; }
});
6. Sử dụng Drag&Drop trong Adapter của RecyclerView: 
Có thể sử dụng hoàn toàn không cần đến thư viện thứ 3 để hoàn thành việc drag và drop, thực thi sau liền tác vụ. Nhược điểm là sẽ block user interaction behind one time, tức là mỗi lần state của item row chỉ hoàn thành 1 việc mà không đa nhiệm (tính riêng từng item).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
        // remove item from adapter
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
final int fromPos = viewHolder.getAdapterPosition(); final int toPos = target.getAdapterPosition();
// move item in `fromPos` to `toPos` in adapter. return true;// true if moved, false otherwise
} };
7. Thêm Header Footer vào trong RecyclerView:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
package prj.ngoctan.myapplication;

public class SampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int FOOTER_VIEW = 1;

    // Define a view holder for Footer view
    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Now define the viewholder for Normal list item
    public class NormalViewHolder extends ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
// Do whatever you want on clicking the normal items
                }
            });
        }
    }

    // And now in onCreateViewHolder you have to pass the correct view
// while populating the list item.
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent,
                    false);
            return vh;
        }
        v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_normal, parent, false);
        NormalViewHolder vh = new NormalViewHolder(v);
        return vh;
    }

    // Now bind the viewholders in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        try {
            if (holder instanceof NormalViewHolder) {
                NormalViewHolder vh = (NormalViewHolder) holder;
                vh.bindView(position);
            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
    @Override
    public int getItemCount() {
        if (data == null) {
            return 0;
        }
        if (data.size() == 0) {
//Return 1 here to show nothing return 1;
        }
        // Add extra view to show the footer view
        return data.size() + 1;
    }

    // Now define getItemViewType of your own.
    @Override
    public int getItemViewType(int position) {
        FooterViewHolder vh = new FooterViewHolder(v);
        if (position == data.size()) {

// This is where we'll add footer.
            return FOOTER_VIEW;
        }
        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
    public class ViewHolder extends RecyclerView.ViewHolder { // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindView(int position) {
// bindView() method to implement actions
        }
    }
}
HappyCoding!!!

Nhận xét

Bài đăng phổ biến từ blog này

[Android-02] LayoutParams trong Android