Mình đã tạo 1 thư viện Swipeable RecyclerView Android như thế nào? – Tuân FADBG

Mình đã tạo 1 thư viện Swipeable RecyclerView Android như thế nào?

Các thư viện tôi đã làm

  • Mình đã tạo 1 thư viện Swipeable RecyclerView Android như thế nào?

Cách tạo thư viện Swipeable RecyclerView trên Android.

Ý tưởng:

IOS thì hoàn toàn có sẵn, còn Android thì không. Do đó mình bắt tay vào làm thôi.

Demo như thế này nhé. Link github của mình nếu muốn tích hợp nó vào app của bạn: Swipleable RecyclerView Android

Cách hoạt động:

Nó là 1 list thì đương nhiên mình sẽ phải sử dụng recyclerview rồi, mình sẽ tạo 1 cái adapter được thừa kế từ RecyclerView.Adapter mặc định, sau này muốn sử dụng chỉ cần kế thừa cái adapter đó là có thể dùng được. Mình đặt tên nó là SwipeableRecyclerViewAdapter và đương nhiên mình sẽ tạo 1 cái ViewHolder nữa và nó sẽ thừa kế từ RecyclerView.ViewHolder, tên là SwipeableViewHolder

Đầu tiên cùng phân tích 1 tí, mình thấy nó gồm 2 phần khác nhau, 1 phần trên sẽ chuyển động theo chiều vuốt và phần bên dưới chỉ đứng im, khi vuốt sẽ dần hiển thị ra, do đó mình sẽ tạo lần lượt 2 layout, layout phía trên để có thể vuốt, layout phía dưới sẽ cố định để chứa các button, text hay gì đó.

Sự kiện vuốt mình sẽ chỉ nhận ở layout trên và bên dưới chỉ cần đứng im và nhận các sự kiện click này nọ thôi.

Bắt tay vào làm luôn nhé.

Mình viết bài này ngay sau khi mình xuất bản thư viện, sau này nếu mình mở rộng ra nữa thì sẽ thay đổi, nên nếu các bạn muốn xem code của mình thì vào link thư viện của mình để xem nhé.

Trước hết mình tạo class SwipeableRecyclerViewAdapter và mình sẽ phải đặt ràng buộc cho các loại class trong nó, mình bắt buộc adapter phải được kế thừa từ SwipeableViewHolder thì mới có thể sử dụng được, vì nếu không ràng buộc thì chắc chắn nó không thể hoạt động được, còn C là loại của đối tượng được truyền vào adapter.

// SwipeableRecyclerViewAdapter
public abstract class SwipeableRecyclerViewAdapter<C, SVH extends SwipeableViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 

}

Bây giờ mình sẽ phải tạo 2 hàm riêng để tạo được 2 cái view và gắn nó vào 1 layout, 1 hàm để tạo ViewHolder sau khi đã gắn các layout vào nha. Mình phải truyền vào mỗi hàm có viewType để có thể hiển thị nhiều dạng layout khác nhau, tăng thêm sự linh động cho thư viện.

// SwipeableRecyclerViewAdapter
public abstract View onCreateMainView(@NonNull ViewGroup viewGroup, int viewType);

public abstract View onCreateBelowView(@NonNull ViewGroup viewGroup, int viewType);

public abstract SVH onCreateCustomViewHolder(@NonNull ViewGroup viewGroup, int viewType);

Trong hàm onCreateViewHolder mà mình cần override thì mình phải gắn tạo 1 cái view cha mặc định, mình chọn RelativeLayout, sau khi lấy được 2 view từ onCreateMainView và onCreateBelowView thì mình sẽ gắn nó lại và RelativeLayout và tạo viewHolder cho nó bằng onCreateCustomViewHolder. Nhớ là phải đưa below view vào trước để nó nằm bên dưới nhé.

// SwipeableRecyclerViewAdapter
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
    RelativeLayout relativeLayout = new RelativeLayout(context);

    View belowView = onCreateBelowView(viewGroup, viewType);
    View mainView = onCreateMainView(viewGroup, viewType);

    relativeLayout.addView(belowView);
    relativeLayout.addView(mainView);

    SVH svh = onCreateCustomViewHolder(relativeLayout, viewType);
    return svh;
}

Tạm thời như thế đã, bây giờ mình sẽ chuyển sang ViewHolder.

ViewHolder chỉ đơn giản là thừa kết lại RecyclerView.ViewHolder và phải chứa một số hàm tạo animation để có thể di chuyển được layout.

public class SwipeableViewHolder extends RecyclerView.ViewHolder {

}

Tiếp tục phân tích 1 tí nhé, để có thể di chuyển được view thì mình sẽ dùng các hàm này view.animate().x(int xAsis); trong đó xAsis là vị trí mà mình muốn layout di chuyển đến đó theo trục x, đơn vị là pixel nhé.

Vị trí mà mình di chuyển tới thì có thể ở bất kỳ đâu, tuỳ vào below view mà mình tạo ra. Do đó giá trị này sẽ do người dùng nhập vào với đơn vị là dp, và mình sẽ chuyển sang px và lưu vào view holder.

// SwipeableViewHolder
View main;
private ArrayList<Float> positionMoveInLeft = new ArrayList<>();
private ArrayList<Float> positionMoveInRight = new ArrayList<>();

public void swipeLeft(boolean isUp) {
...
}

public void swipeRight(boolean isUp) {
...
}

 

Tiếp theo để nhận được sự kiện vuốt thì mình cần tạo 1 class đó là OnSwipeTouchListener kế thừa từ View.OnTouchListener. Trong hàm này class này mình làm khá đơn giản, chỉ đơn giản là xem vị trí chạm và đưa ra đang vuốt về hướng nào.

public class OnSwipeTouchListener implements View.OnTouchListener {

    private int x1, x2;
    private static final int SWIPE_THRESHOLD = 30;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case (MotionEvent.ACTION_DOWN):
                x1 = (int) event.getX();
                return false;
            case (MotionEvent.ACTION_MOVE):
                x2 = (int) event.getX();
                if (x2 - x1 > SWIPE_THRESHOLD) {
                    return onSwipeRight();
                } else if (x1 - x2 > SWIPE_THRESHOLD) {
                    return onSwipeLeft();
                }
                return false;
            case (MotionEvent.ACTION_UP):
                return false;
            case (MotionEvent.ACTION_CANCEL):
                return false;
            case (MotionEvent.ACTION_OUTSIDE):
                return false;
        }
        return false;
    }

    public boolean onSwipeRight() {
        return false;
    }

    public boolean onSwipeLeft() {
        return false;
    }
}

Sau khi đã có adapter rồi thì bây giờ mình sẽ cho main view lắng nghe sự kiện touch. Nếu không có cái này thì không có vuốt ve gì hết đâu nhé.

Trong SwipeableRecyclerViewAdapter vì được kế thừa RecyclerView.Adapter nên mình phải override lại hàm onBindViewHolder

// SwipeableRecyclerViewAdapter
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
    // người dùng sẽ override lại hàm này để kết nối ViewHolder của mình và layout
    onBindCustomViewHolder((SVH) viewHolder, position); 
    // khi mới được khởi tạo thì mặc định đưa layout trên vào vị trí 0, 
    // nếu không khi tái sử dụng lại view thì sẽ bị lỗi, mình chưa tìm được cách fix nào hay hơn
    ((SVH) viewHolder).main.animate().x(0).setDuration(0);
    // hàm này set sự kiện cho main view
    setSwipe((SwipeableViewHolder) viewHolder, position);
}

public abstract View onCreateMainView(@NonNull ViewGroup viewGroup, int viewType);

public abstract View onCreateBelowView(@NonNull ViewGroup viewGroup, int viewType);

public abstract SVH onCreateCustomViewHolder(@NonNull ViewGroup viewGroup, int viewType);

public abstract ArrayList<Float> getPositionLeft(int viewType); // nhận vị trí vuốt đến ở bên trái

public abstract ArrayList<Float> getPositionRight(int viewType); // cái này là ở bên phải 

public abstract void onBindCustomViewHolder(SVH viewHolder, int position);

public void setOnMainClickListener(View.OnClickListener onMainClickListener) {
    this.onMainClickListener = onMainClickListener;
}

private void setSwipe(SwipeableViewHolder viewHolder, int i) {
    // mặc định mình phải set onClick cho nó, để có thể nhận được cái sự kiện của onTouch
    // để biết main view là gì thì mình phải truyền id của nó vào trong ViewHolder, 
    // nên viewHolder.main là main view rồi nhé
    viewHolder.main.setOnClickListener(onMainClickListener); 

    viewHolder.main.setOnTouchListener(new OnSwipeTouchListener() {
        public boolean onSwipeRight() {
            viewHolder.swipeRight(true);
            return true;
        }

        public boolean onSwipeLeft() {
            viewHolder.swipeLeft(true);
            return true;
        }
    });
}

Thực ra còn rất nhiều vấn đề, nhưng mình chỉ nói sơ bộ như thế này để bạn có thể hiểu cách làm của mình.

Mình mới làm phần này nên vẫn chưa có nhiều cách xử lí ổn nhất. Nếu các bạn có cách xử lí nào hay hơn thì có thể comment phía dưới nhé để mình có thể nâng cấp khiến nó ổn định và dễ sử dụng hơn nhé. Bạn muốn xem full code thì vào link github để xem nhé.

Công việc cuối cùng của mình là xuất bản nó thành thư viện nhé. Mình đã viết 1 bài hướng dẫn cực kỳ đơn giản bằng việc sử dụng Jitpack rồi nhé. Bạn xem cách xuất bản thư viện android nhé.

Hôm nay cũng là một ngày gần cuối năm 2018 rồi, chúc mọi người 1 năm mới vui vẻ, gặt hái nhiều thành công và hạnh phúc nhé.