Android ---- 侧滑删除菜单的实现

环境说明

操作系统:Win10
IDE:Android Studio



实现效果在这里插入图片描述

实现代码

一、布局文件

(一)item_content.xml【ListView的Item的内容】
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:text="Item1"
    android:textSize="25sp"
    android:gravity="center"
    android:textColor="@android:color/black">
</TextView>



显示效果

在这里插入图片描述


(二)item_menu.xml【Item的侧滑删除按钮】
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="70dp"
    android:layout_height="60dp"
    android:gravity="center"
    android:background="@android:color/holo_red_light"
    android:text="删除"
    android:textSize="25sp"
    android:textColor="#FFFFFFFF">

</TextView>



显示效果

在这里插入图片描述


(三)item_main.xml【一个完整Item】

SlideLayout为自定义Layout

<?xml version="1.0" encoding="utf-8"?>
<com.example.slidelayout.SlideLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp">

    <include android:id="@+id/item_content" layout="@layout/item_content"/>
    <include android:id="@+id/item_menu" layout="@layout/item_menu"/>

</com.example.slidelayout.SlideLayout>



显示效果

在这里插入图片描述

(三)activity_main.xml【包含一个ListView】
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>





二、Java代码

(一)、SlideLayout.java

package com.example.slidelayout;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Scroller;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class SlideLayout extends FrameLayout {

    private static final String TAG=SlideLayout.class.getSimpleName();

    private View contentView; // 内容视图
    private View menuView;    // 菜单视图

    private int contentWidth;   // 内容视图的高度
    private int contentHeight;  // 内容视图的宽度
    private int menuWidth;      // 菜单视图的宽度
    private int menuHeight;     // 菜单视图的宽度
    private Scroller scroller;  // 专门用于处理滚动效果的工具类
    private float downX;        // 按下的x坐标
    private float downY;
    private float endX;
    private float endY;         // 当前的Y坐标,中间变化
    private float startX;       // 开始的x坐标,中间变化

    /**
     * xml布局方式加载使用
     * @param context
     * @param attrs
     */
    public SlideLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        scroller =new Scroller(context);
    }

    /**
     * XML布局被加载完后,就会回调onFinshInfalte这个方法,在这个方法中我们可以初始化控件和数据。
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        contentView = findViewById(R.id.item_content);
        menuView = findViewById(R.id.item_menu);
    }

    /**
     * 设置子视图的位置
     * @param changed
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        menuView.layout(contentWidth,0,contentWidth+menuWidth,menuHeight);
    }

    /**
     * 测量子视图的宽度和高度
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        contentHeight = contentView.getMeasuredHeight();
        contentWidth = contentView.getMeasuredWidth();
        menuHeight = menuView.getMeasuredHeight();
        menuWidth = menuView.getMeasuredWidth();
    }


    /**
     * 触摸事件
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                // 记录最初的x坐标
                startX = event.getX();
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                endX = event.getX();
                endY = event.getY();

                // dx为横向发生的完整距离
                float dx = Math.abs(endX - downX);
                if (dx > 0 ) { // 只要发生横向滑动,纵向禁止滑动
                    getParent().requestDisallowInterceptTouchEvent(true);
                }

                // 滑动距离
                float dis = endX-startX;
                Log.e("onTouchEvent:", String.valueOf(dis));
                // 向左滑为负值,通过下面一行代码转换至滑至的x坐标
                dis = getScrollX()-dis;
                if (dis < 0){ // 坐标最小为0
                    dis = 0;
                }else if (dis > menuWidth){ // 最多向左滑menuWidth距离
                    dis = menuWidth;
                }
                scrollTo((int) dis,getScrollY());

                // 更新开始x坐标
                startX = event.getX();
                break;
            case MotionEvent.ACTION_UP: // 如果滑出距离小于菜单宽度一半,菜单自动关闭
                int totalX = getScrollX(); // 视图显示部分的左边缘
                if (totalX < menuWidth/2){
                    closeMenu();
                }else {
                    openMenu();
                }
                break;
        }
        return true;
    }

    /**
     * 拦截触摸事件
     * @param event
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean isIntercept = false; // 最初为不拦截
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downX = startX = event.getX();
                try {
                    if (onStateChangeListener == null){
                        throw new Exception("状态改变监听者未设置");
                    }
                    onStateChangeListener.onDown(SlideLayout.this); // 关闭其他item
                }catch (Exception e){
                    e.printStackTrace();
                }
                break;
            case MotionEvent.ACTION_MOVE:
                endX = event.getX();
                float distance = Math.abs(endX - startX);
                if (distance > 0){ // 横向发生滑动,拦截事件
                    isIntercept = true;
                }
                break;
        }
        return isIntercept;
    }

    /**
     * 打开菜单
     */
    public void openMenu(){
        int distance = menuWidth - getScrollX(); // 剩余未滑动距离
        Log.e("openMenu:", String.valueOf(getScrollX()));
        scroller.startScroll(getScrollX(),getScrollY(),distance,0); // 滑动处理
        invalidate(); // 重绘
        try {
            if (onStateChangeListener == null){
                throw new Exception("状态改变监听者未设置");
            }
            onStateChangeListener.onOpen(SlideLayout.this); // 打开item为当前item
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 关闭菜单
     */
    public void closeMenu(){
        int distance = 0 - getScrollX();
        Log.e("closeMenu:", String.valueOf(getScrollX()));
        scroller.startScroll(getScrollX(),getScrollY(),distance,0);
        invalidate();
        try {
            if (onStateChangeListener == null){
                throw new Exception("状态改变监听者未设置");
            }
            onStateChangeListener.onClose(SlideLayout.this);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()){ // 滑动未结束
            scrollTo(scroller.getCurrX(),scroller.getCurrY()); // 继续滑动到当前位置
            invalidate();
        }
    }

    /**
     * 监听菜单打开关闭
     */
    public interface OnStateChangeListener{
        void onClose(SlideLayout layout);
        void onDown(SlideLayout layout);
        void onOpen(SlideLayout layout);
    }

    private OnStateChangeListener onStateChangeListener;

    public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) {
        this.onStateChangeListener = onStateChangeListener;
    }
}



(二)、MyContent.java

package com.example.slidelayout;

public class MyContent {
    private String name;

    public MyContent(String name){ this.name=name; }

    public String getName() { return name; }

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



(三)、MainActivity.java

package com.example.slidelayout;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    private ListView listView;
    private ArrayList<MyContent> myContents;
    private SlideLayout slideLayout;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setMyContents();
        initView();
    }

    /**
     * 设置数据
     */
    private void setMyContents(){
        myContents = new ArrayList<>();
        for (int i=1;i<100;++i){
            MyContent myContent = new MyContent("Item"+i);
            myContents.add(myContent);
        }
    }

    /**
     * 初始化视图
     */
    private void initView(){
        listView = findViewById(R.id.lv_main);
        listView.setAdapter(new MyAdapter()); // 设置适配器
    }


    /**
     * 适配器
     */
    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return myContents.size();
        }

        @Override
        public Object getItem(int position) {
            return myContents.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if (convertView == null){ // 若不存在,则创建一个新的视图
                convertView = View.inflate(MainActivity.this,R.layout.item_main,null);
                viewHolder = new ViewHolder();
                viewHolder.item_content = convertView.findViewById(R.id.item_content);
                viewHolder.item_menu = convertView.findViewById(R.id.item_menu);
                convertView.setTag(viewHolder);
            }else { // 直接复用
                viewHolder = (ViewHolder) convertView.getTag();
            }
            viewHolder.item_content.setText(myContents.get(position).getName());

            viewHolder.item_content.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String info = myContents.get(position).getName();
                    Toast.makeText(MainActivity.this,info,Toast.LENGTH_SHORT).show();
                }
            });

            viewHolder.item_menu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    SlideLayout slideLayout = (SlideLayout) v.getParent(); // 获取父亲视图
                    slideLayout.closeMenu(); // 关闭当前菜单
                    myContents.remove(position); // 移除
                    notifyDataSetChanged(); // 通知更变
                }
            });

            SlideLayout slideLayout = (SlideLayout) convertView;
            slideLayout.setOnStateChangeListener(new MyOnStateChangeListener()); // 设置监听者

            return convertView;
        }
    }

    static class ViewHolder {
        TextView item_content;
        TextView item_menu;
    }

    
    private class MyOnStateChangeListener implements SlideLayout.OnStateChangeListener {
        @Override
        public void onClose(SlideLayout layout) { // 菜单关闭后,选中item为空
            if (slideLayout == layout){
                slideLayout = null;
            }
        }

        @Override
        public void onDown(SlideLayout layout) {
            if (slideLayout != null && slideLayout != layout) { // 当打开其他item的菜单时,关闭已打开的菜单
                slideLayout.closeMenu();
            }
        }

        @Override
        public void onOpen(SlideLayout layout) {
            if (slideLayout == null){
                slideLayout = layout;
            }
        }
    }
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值