Android开发-自定义View-AndroidStudio(十六)侧滑删除

转载请注明出处: http://blog.csdn.net/iwanghang/article/details/53929510
觉得博文有用,请点赞,请评论,请关注,谢谢!~


项目源码下载: http://download.csdn.net/detail/iwanghang/9724693


老规矩,先上GIF动态图,看个效果,如果符合你的项目或者确定你要了解的内容,再往下看吧:
其实最近几篇博客写得不是很好,代码已经不太适合初学者来阅读,很多地方刚接触Android的同学可能还需要查资料,才能理解。
从这篇博文开始,博主把所有可能需要查资料的地方,都写在注释里,起码看了就明白意思,减少大家查资料的时间。
争取做到,复制代码就去用,不懂的地方几乎不用查资料,直接从注释里就能得到答案。让我们共同进步~



MainActivity.java:
package com.iwanghang.slidedemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ListView lv_main;
    private ArrayList<MyBean> myBeans;
    private MyAdapter myAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv_main = (ListView) findViewById(R.id.lv_main);

        // 设置适配器
        // 准备数据
        myBeans = new ArrayList<>();
        for(int i=0;i<100;i++){
            myBeans.add(new MyBean("iwanghang~"+i));
        }
        myAdapter = new MyAdapter();
        lv_main.setAdapter(myAdapter);
    }

    class MyAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return myBeans.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

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

        /**
         * ViewHolder的解释:
         * 1、只是一个静态类,不是Android的API方法。
         * 2、它的作用就在于减少不必要的调用findViewById,然后把对底下的控件引用存在ViewHolder里面,再在
         * View.setTag(holder)把它放在view里,下次就可以直接取了。
         */
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if(convertView == null){
                convertView = View.inflate(MainActivity.this,R.layout.item_main,null);
                viewHolder = new ViewHolder();
                viewHolder.item_content = (TextView) convertView.findViewById(R.id.item_content);
                viewHolder.item_menu = (TextView) convertView.findViewById(R.id.item_menu);
                /**
                 * setTag
                 * 把查找的view缓存起来方便多次重用
                 * 相当于给View对象的一个标签。XX标签可以是任何内容,我们这里把他设置成了一个对象XX。
                 *
                 * Tag的作用就是设置标签,标签可以是任意玩意。
                 * 以及convertView是如何在程序中使代码运行变的效率的:利用缓存convertView尽可能少实例化
                 * 同样结构体的对象;
                 */
                convertView.setTag(viewHolder);
            }else{
                viewHolder = (ViewHolder) convertView.getTag();
            }

            // 根据位置得到内容
            final MyBean myBean = myBeans.get(position);
            viewHolder.item_content.setText(myBean.getName());

            viewHolder.item_content.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    MyBean myBean1 = myBeans.get(position);
                    Toast.makeText(MainActivity.this, myBean1.getName(), Toast.LENGTH_SHORT).show();
                    System.out.println("MainActivity---onClick");
                }
            });

            viewHolder.item_menu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    /**
                     * getParent() 获取父组件
                     * 获取爷爷组件,可以使用getParent().getParent()
                     */
                    SlideLayout slideLayout = (SlideLayout) view.getParent();
                    slideLayout.closeMenu();
                    myBeans.remove(myBean);
                    notifyDataSetChanged();
                    System.out.println("MainActivity---remove");
                }
            });

            SlideLayout slideLayout = (SlideLayout) convertView;
            slideLayout.setOnStateChangeListener(new MyOnStateChangeListener());
            return convertView;
        }
    }

    private SlideLayout slideLayout;

    class MyOnStateChangeListener implements SlideLayout.OnStateChangeListener {

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

        @Override
        public void onDown(SlideLayout layout) {
            if(slideLayout!=null && slideLayout!=layout){
                slideLayout.closeMenu();
            }
        }

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

    }

    /**
     * ViewHolder的解释:
     * 1、只是一个静态类,不是Android的API方法。
     * 2、它的作用就在于减少不必要的调用findViewById,然后把对底下的控件引用存在ViewHolder里面,再在
     * View.setTag(holder)把它放在view里,下次就可以直接取了。
     */
    static class ViewHolder{
        TextView item_content;
        TextView item_menu;
    }
}
SlideLayout.java:
package com.iwanghang.slidedemo;

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;

public class SlideLayout extends FrameLayout {

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

    /**
     * 滚动者
     */
    private Scroller scroller;

    /**
     * Content的宽
     */
    private int contentWidth;
    private int menuWidth;
    private int viewHeight; // 他们的高都是相同的

    public SlideLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        scroller = new Scroller(context);
    }

    /**
     * 当布局文件加载完成的时候回调这个方法
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        contentView = getChildAt(0);
        menuView = getChildAt(1);
    }

    /**
     * 在测量方法里,得到各个控件的高和宽
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        contentWidth = contentView.getMeasuredWidth();
        menuWidth = menuView.getMeasuredWidth();
        viewHeight = getMeasuredHeight();
    }

    @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, viewHeight);
    }

    private float startX;
    private float startY;
    private float downX; // 只赋值一次
    private float downY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                 // 1.按下记录坐标
                 downX = startX = event.getX();
                 downY = startY = event.getY();
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE");
                // 2.记录结束值
                float endX = event.getX();
                float endY = event.getY();
                // 3.计算偏移量
                float distanceX = endX - startX;

                int toScrollX = (int) (getScrollX() - distanceX);
                if (toScrollX < 0) {
                    toScrollX = 0;
                } else if (toScrollX > menuWidth) {
                    toScrollX = menuWidth;
                }

                scrollTo(toScrollX, getScrollY());

                startX = event.getX();
                startY = event.getY();
                // 在X轴和Y轴滑动的距离
                float DX = Math.abs(endX-downX);
                float DY = Math.abs(endY-downY);
                if(DX>DY && DX>8){
                    // 水平方向滑动
                    // 响应侧滑
                    // 反拦截-事件给SlideLayout
                    getParent().requestDisallowInterceptTouchEvent(true);
                }

                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_UP");
                int totalScrollX = getScrollX();//偏移量
                if(totalScrollX < menuWidth/2){
                    // 关闭Menu
                    closeMenu();
                }else{
                    // 打开Menu
                    openMenu();
                }
                break;
        }

        return true;
    }

    /**
     * true:拦截孩子的事件,但会执行当前控件的onTouchEvent()方法
     * false:不拦截孩子的事件,事件继续传递
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercept = false;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 1、按下记录坐标
                downX = startX = event.getX();
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN");
                if(onStateChangeListener != null){
                    onStateChangeListener.onDown(this);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE");
                // 2、记录结束值
                float endX = event.getX();
                float endY = event.getY();
                // 3、计算偏移量
                float distanceX = endX - startX;

                startX = event.getX();
                // 在X轴和Y轴滑动的距离
                float DX = Math.abs(endX-downX);
                if(DX>8){
                    intercept = true;
                }

                break;
            case MotionEvent.ACTION_UP:
                break;
        }

        return intercept;
    }

    /**
     * 打开menu
     */
    public void openMenu() {
        // --->menuWidth
        int distanceX = menuWidth - getScrollX();
        scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
        invalidate(); // 强制刷新
        System.out.println("SlideLayout---openMenu");
        if(onStateChangeListener != null){
            onStateChangeListener.onOpen(this);
        }
    }

    /**
     * 关闭menu
     */
    public void closeMenu() {
        // --->0
        int distanceX = 0 - getScrollX();
        scroller.startScroll(getScrollX(), getScrollY(), distanceX, getScrollY());
        invalidate(); // 强制刷新
        System.out.println("SlideLayout---closeMenu");
        if(onStateChangeListener != null){
            onStateChangeListener.onClose(this);
        }
    }

    /**
     * computeScroll:主要功能是计算拖动的位移量、更新背景、设置要显示的屏幕(setCurrentScreen(mCurrentScreen);)。
     * 重写computeScroll()的原因:调用startScroll()是不会有滚动效果的,只有在computeScroll()获取滚动情况,做出滚动的响应
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if(scroller.computeScrollOffset()){
            scrollTo(scroller.getCurrX(),scroller.getCurrY());
            invalidate();
            System.out.println("SlideLayout---computeScroll");
        }
    }

    /**
     * 接口回调
     * 监听SlideLayout状态的改变
     */
    public interface OnStateChangeListener{
        void onClose(SlideLayout layout);
        void onDown(SlideLayout layout);
        void onOpen(SlideLayout layout);
    }

    private  OnStateChangeListener onStateChangeListener;

    /**
     * 设置SlideLayout状态的监听
     */
    public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) {
        this.onStateChangeListener = onStateChangeListener;
    }
}
MyBean.java:
package com.iwanghang.slidedemo;

public class MyBean {
    private String name;

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

    public String getName() {
        return name;
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.iwanghang.slidedemo.MainActivity">

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
item_main.xml
<?xml version="1.0" encoding="utf-8"?>
<com.iwanghang.slidedemo.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.iwanghang.slidedemo.SlideLayout>
item_content.xml
<?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:background="#44000000"
    android:gravity="center"
    android:text="Content"
    android:textColor="#000000"
    android:textSize="25sp">
</TextView>
item_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    android:background="#22000000"
    android:gravity="center"
    android:text="Delete"
    android:textColor="#ff0000"
    android:textSize="25sp">
</TextView>


转载请注明出处: http://blog.csdn.net/iwanghang/article/details/53929510



欢迎移动开发爱好者交流
沈阳或周边城市公司有意开发Android,请与我联系
联系方式

微信:iwanghang
QQ:413711276
邮箱:iwanghang@qq.com



项目源码下载: http://download.csdn.net/detail/iwanghang/9724693


觉得博文有用,请点赞,请评论,请关注,谢谢!~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值