Android----RecyclerView的侧滑菜单

作者: pgydbh | 来源:发表于2018-08-15 20:14 被阅读13次

目录

无标题1.png

演示

gif5新文件 (1).gif

使用方式

①加入SwipeRecycler.java

无标题.png

②布局

    <com.github.pgycode.swiperecycler.SwipeRecycler
        android:layout_width="match_parent"
        android:id="@+id/recycler"
        android:layout_height="match_parent">

    </com.github.pgycode.swiperecycler.SwipeRecycler>

③子view布局样例

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_height="70dp">
        <!--假设这个textview是主view-->
        <TextView
            android:id="@+id/txt"
            android:layout_width="360dp"
            android:text="测试swipeRecycler"
            android:gravity="center"
            android:textSize="17sp"
            android:textStyle="bold"
            android:textColor="#000000"
            android:background="#ffffff"
            android:layout_height="70dp" />

        <Button
            android:layout_width="90dp"
            android:layout_height="70dp"
            android:gravity="center"
            android:text="置顶"
            android:id="@+id/btn_top"
            android:background="#c8c7c7"
            android:textColor="#ffffff"
            android:textSize="17sp" />

        <Button
            android:layout_width="90dp"
            android:layout_height="70dp"
            android:gravity="center"
            android:text="删除"
            android:id="@+id/btn_del"
            android:background="#e94637"
            android:textColor="#ffffff"
            android:textSize="17sp" />
    </LinearLayout>
    <!--这是分割线-->
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/colorPrimary"/>
</LinearLayout>

④子view的尺寸----这里要分别设置子view与子view内部主view的宽度。

public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, recycler, false);
    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
    layoutParams.width = recycler.getScreenWidth() + recycler.dp2px(180);
    view.setLayoutParams(layoutParams);

    //这里假设这个txt是主view
    View main = view.findViewById(R.id.txt);
    ViewGroup.LayoutParams mainLayoutParams = main.getLayoutParams();
    mainLayoutParams.width = recycler.getScreenWidth();
    main.setLayoutParams(mainLayoutParams);

    return new ViewHolder(view);
}

⑤设置点击事件记得先关闭菜单

holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        recycler.closeEx();
        Snackbar.make(MainActivity.this.findViewById(R.id.root), position + "", 
           Snackbar.LENGTH_SHORT).show();
    }
});

思路

1.整体思路

设置paddingLeft为负值,实现弹出菜单。

    //移动距离
    public int distance = 0;
2.如何区分横向滑动和竖向滑动。

滑动开始时设置阈值为50去判断,是什么滑动,只有大于50才判定是什么滑动。

    //是否在拖动
    public boolean isSwipe = false;

    //是否在滚动
    private boolean isSroll = false;
3.处理滑动时候的多种情况。

分情况处理
①向左 在里面
②向左 在外面
③向右 在外面
④向右 在里面

    //是否需要向右
    private boolean toRight = false;

    //是否需要向左
    private boolean toLeft = false;
4.滑动事件与点击事件冲突。

滑动事件的状态处理写在dispatchTouchEvent里面,控制下发事件
滑动事件的页面处理写在onTouchEvent里面。

5.view宽度如何设置。

自行设置

View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, recycler, false);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = recycler.getScreenWidth() + recycler.dp2px(180);
view.setLayoutParams(layoutParams);

View main = view.findViewById(R.id.txt);
ViewGroup.LayoutParams mainLayoutParams = main.getLayoutParams();
mainLayoutParams.width = recycler.getScreenWidth();
main.setLayoutParams(mainLayoutParams);

代码

main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/root"
    android:layout_height="match_parent">

    <com.github.pgycode.swiperecycler.SwipeRecycler
        android:layout_width="match_parent"
        android:id="@+id/recycler"
        android:layout_height="match_parent">

    </com.github.pgycode.swiperecycler.SwipeRecycler>

</RelativeLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:layout_height="70dp">
        <!--假设这个textview是主view-->
        <TextView
            android:id="@+id/txt"
            android:layout_width="360dp"
            android:text="测试swipeRecycler"
            android:gravity="center"
            android:textSize="17sp"
            android:textStyle="bold"
            android:textColor="#000000"
            android:background="#ffffff"
            android:layout_height="70dp" />

        <Button
            android:layout_width="90dp"
            android:layout_height="70dp"
            android:gravity="center"
            android:text="置顶"
            android:id="@+id/btn_top"
            android:background="#c8c7c7"
            android:textColor="#ffffff"
            android:textSize="17sp" />

        <Button
            android:layout_width="90dp"
            android:layout_height="70dp"
            android:gravity="center"
            android:text="删除"
            android:id="@+id/btn_del"
            android:background="#e94637"
            android:textColor="#ffffff"
            android:textSize="17sp" />
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/colorPrimary"/>
</LinearLayout>
SwipeActivity.Java

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class SwipeRecycler extends RecyclerView {

    //移动距离
    public int distance = 0;

    //是否在拖动
    public boolean isSwipe = false;

    //是否需要向右
    private boolean toRight = false;

    //是否需要向左
    private boolean toLeft = false;

    //是否在滚动
    private boolean isSroll = false;

    //开始的x
    private float startx;

    //开始的y
    private float starty;

    //现在的
    private int add = -1;

    //多出尺寸
    private int exSize;

    private LinearLayoutManager layoutManager;
    private Context context;

    public SwipeRecycler(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        layoutManager = new LinearLayoutManager(context);
        setLayoutManager(layoutManager);
        this.context = context;

        //确认滚动停止
        addOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == SCROLL_STATE_IDLE){
                    isSroll = false;
                }
            }
        });
    }


    /**
     * 返回 false 不作处理 但是也不禁止它上传
     * @param e
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (e.getAction() == MotionEvent.ACTION_MOVE){
            int movex = (int) (e.getX() - startx);
            int movey = (int) (e.getY() - starty);

            //滑动范围过小,不作用于view
            if (Math.abs(movey) < 50 && Math.abs(movex) < 50) {
                return false;
            }
            if (isSroll){
                return super.onTouchEvent(e);
            }
            if (isSwipe && toLeft){
                distance = movex;
                move();
            }

            if (isSwipe && toRight){
                distance = -exSize + movex;
                move();
            }
            return false;
        }
        if (isSwipe){
            return false;
        }
        return super.onTouchEvent(e);
    }

    private void move(){
        if (distance > 0){
            distance = 0;
        } if (distance < -exSize){
            distance = -exSize;
        }
        if (add != -1) {
            View view = findViewHolderForAdapterPosition(add).itemView;
            setPaddingLeft(view, distance);
        }
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (isSwipe || isSroll){
            return true;
        }
        return super.onInterceptTouchEvent(e);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        /**
         * 按下事件
         * 只记录按下之后的坐标
         */
        if (e.getAction() == MotionEvent.ACTION_DOWN){
            startx = e.getX();
            starty = e.getY();
        }

        /**
         * 移动事件
         */
        else if (e.getAction() == MotionEvent.ACTION_MOVE){
            int movex = (int) (e.getX() - startx);
            int movey = (int) (e.getY() - starty);

            if (!isSwipe && !isSroll && Math.abs(movex) > 50){
                isSwipe = true;
                //向右 在外面
                if (movex > 0 && distance < 0){
                    toRight = true;
                    toLeft = false;
                }
                //向左 在里面
                else if (movex < 0 && distance == 0){
                    int temp = getPosition(e);
                    if (temp != -1){
                        add = temp;
                        exSize = findViewHolderForAdapterPosition(add).itemView.getWidth() - getWidth();
                        toRight = false;
                        toLeft = true;
                    }
                }
                //向左 在外面
                else if (movex < 0 && distance < 0){
                    int temp = getPosition(e);
                    if (temp != -1 && temp != add) {
                        playLastBack();
                        add = temp;
                        exSize = findViewHolderForAdapterPosition(add).itemView.getWidth() - getWidth();
                        toRight = false;
                        toLeft = true;
                    }
                } else {
                    toRight = false;
                    toLeft = false;
                }
            } else if (!isSroll && !isSwipe && Math.abs(movey) > 50){
                isSroll = true;
                closeEx();
            }
            System.out.println(isSroll + " " + isSwipe + " " + toLeft + " " + toRight + " " + distance + " " + movex + " " + movey);
        } else  {
            if (toRight){
                if (distance > -2 * exSize / 3){
                    playBack(distance, 0);
                } else {
                    playBack(distance, -exSize);
                }
            }
            if (toLeft){
                if (distance < -exSize / 3){
                    playBack(distance, -exSize);
                } else {
                    playBack(distance, 0);
                }
            }
            else{
                initData();
            }
        }
        return super.dispatchTouchEvent(e);
    }

    private void playBack(int start, final int end){
        final ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.setDuration(Math.abs(end - start) / 3);
        final View view = findViewHolderForAdapterPosition(add).itemView;
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                if (add != -1) {
                    setPaddingLeft(view, (Integer) valueAnimator.getAnimatedValue());
                    distance = (int) valueAnimator.getAnimatedValue();
                }
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                if (end == 0){
                    initData();
                    add = -1;
                } else {
                    initData();
                }
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        animator.start();
    }


    public void playLastBack(){
        final View view = findViewHolderForAdapterPosition(add).itemView;
        if (view != null) {
            final ValueAnimator animator = ValueAnimator.ofInt(-exSize, 0);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    setPaddingLeft(view, (Integer) valueAnimator.getAnimatedValue());
                }
            });
            animator.start();
        }
    }

    private int getPosition(MotionEvent e){
        int f = layoutManager.findFirstVisibleItemPosition();
        int l = layoutManager.findLastVisibleItemPosition();
        for (int i = f; i <= l; i++){
            View view = findViewHolderForAdapterPosition(i).itemView;
            int[] location = new int[2];
            view.getLocationOnScreen(location);
            if (e.getY() < location[1] && e.getY() > location[1] - view.getHeight() && e.getX() > location[0] && e.getX() < location[0] + view.getWidth()) {
                return i;
            }
        }
        return -1;
    }

    private void initData(){
        isSwipe = false;
        startx = 0;
        starty = 0;
        toRight = false;
        toLeft = false;
    }


    public void closeEx(){
        ViewHolder viewHolder = findViewHolderForAdapterPosition(add);
        if (viewHolder != null){
            View view = viewHolder.itemView;
            add = -1;
            distance = 0;
            view.setPadding(0, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
        }
    }


    public int getScreenWidth(){
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    /*
     * dp转换成px
     */
    public int dp2px(int dp){
        float scale = context.getResources().getDisplayMetrics().density;
        return (int)(dp * scale + 0.5f);
    }


    private void setPaddingLeft(View view, int left){
        view.setPadding(left, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
    }
}

MainActivity.java
package com.github.pgycode.swiperecycler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.material.snackbar.Snackbar;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private SwipeRecycler recycler;//工具类
    private List<String> strings;//模拟数据

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        strings = new ArrayList<>();
        for (int i = 0; i < 100; i++){
            strings.add("test sentence : " + i);
        }

        recycler = findViewById(R.id.recycler);
        recycler.setAdapter(new RecyclerView.Adapter<ViewHolder>() {
            @NonNull
            @Override
            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, recycler, false);
                ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                layoutParams.width = recycler.getScreenWidth() + recycler.dp2px(180);
                view.setLayoutParams(layoutParams);

                View main = view.findViewById(R.id.txt);
                ViewGroup.LayoutParams mainLayoutParams = main.getLayoutParams();
                mainLayoutParams.width = recycler.getScreenWidth();
                main.setLayoutParams(mainLayoutParams);

                return new ViewHolder(view);
            }

            @Override
            public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
                
                //请注意,为了有良好的体验,请记得每次点击事件生效时,关闭菜单
                //recycler.closeEx();
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        recycler.closeEx();
                        Snackbar.make(MainActivity.this.findViewById(R.id.root), position + "", Snackbar.LENGTH_SHORT).show();
                    }
                });

                holder.txt.setText(strings.get(position));
                holder.btnDel.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        recycler.closeEx();
                        //模拟删除
                        strings.remove(position);
                        notifyItemRemoved(position);
                        notifyItemRangeChanged(0, strings.size());
                    }
                });

                holder.btnTop.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        recycler.closeEx();
                        //模拟置顶
                        String temp = strings.remove(position);
                        strings.add(0, temp);
                        notifyItemChanged(position, 0);
                        notifyItemRangeChanged(0, strings.size());
                    }
                });
            }

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

    private class ViewHolder extends RecyclerView.ViewHolder {

        private Button btnDel;
        private Button btnTop;
        private TextView txt;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            btnDel = itemView.findViewById(R.id.btn_del);
            btnTop = itemView.findViewById(R.id.btn_top);
            txt = itemView.findViewById(R.id.txt);
        }
    }
}

demo

https://github.com/pgyCode/SwipeRecycler

相关文章

网友评论

    本文标题:Android----RecyclerView的侧滑菜单

    本文链接:https://www.haomeiwen.com/subject/gcxgbftx.html