使用SwipeDismissBehavior实现侧滑删除

最近在研究CoordinatorLayout与Behavior发现了有SwipeDismissBehavior这个东西,通过它可以实现侧滑删除。先看效果。

效果

这里写图片描述
使用也很简单,xml文件如下:

xml设置

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    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="trs.com.swipedismissdemo.MainActivity">

    <TextView
        android:id="@+id/tv"
        app:layout_behavior="trs.com.swipedismissdemo.MySwipeDismissBehavior"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@android:color/holo_blue_dark"
        android:gravity="center"
        android:text="Hello World!"
        android:textColor="@android:color/white"
        android:textSize="50sp" />
</android.support.design.widget.CoordinatorLayout>

设置回调


public class MainActivity extends AppCompatActivity {
    TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       // SwipeDismissBehavior
        tv= (TextView) findViewById(R.id.tv);
        ViewGroup.LayoutParams params = tv.getLayoutParams();
        if(params instanceof CoordinatorLayout.LayoutParams){
            CoordinatorLayout.LayoutParams p= (CoordinatorLayout.LayoutParams) params;
            CoordinatorLayout.Behavior behavior = p.getBehavior();
            if(behavior instanceof SwipeDismissBehavior){
                SwipeDismissBehavior sb= (SwipeDismissBehavior) behavior;
                sb.setListener(new SwipeDismissBehavior.OnDismissListener() {
                    @Override
                    public void onDismiss(View view) {
                        Log.i("zgh","onDismiss");
                    }

                    @Override
                    public void onDragStateChanged(int state) {
                        Log.i("zgh","onDragStateChanged state="+state);
                    }
                });
            }
        }
    }
}

结果

这里写图片描述

注意事项

1.在使用xml定义时,最开始我直接使用的是

  app:layout_behavior="android.support.design.widget.SwipeDismissBehavior"

结果程序报错:
这里写图片描述

通过查看behaver的实例化过程的代码

1.首先查看CoordinatorLayout内部的LayoutParams,Behavior就是保存在这里的


public static class LayoutParams extends ViewGroup.MarginLayoutParams {
       /**
        * A {@link Behavior} that the child view should obey.
        */
       Behavior mBehavior;
       ...
}

2.在LayoutParams的构造方法中,通过反射创建了Behavior

LayoutParams(Context context, AttributeSet attrs) {
    super(context, attrs);

    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.CoordinatorLayout_LayoutParams);
    ...
    //判断是否设置了behavior
    mBehaviorResolved = a.hasValue(
            R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
    if (mBehaviorResolved) {
      //根据设置的behavior反射实例化一个behavior
        mBehavior = parseBehavior(context, attrs, a.getString(
                R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
    }

    a.recycle();
}

3.具体的解析过程

// 这里是指定的Behavior的参数类型
static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
        Context.class,
        AttributeSet.class
};

...

static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
    if (TextUtils.isEmpty(name)) {
        return null;
    }

    // 代表了我们指定的那个behavior的完整路径
    final String fullName;
    // 如果是".MyBehavior"
    // 则在前面加上程序的包名
    if (name.startsWith(".")) {
        // Relative to the app package. Prepend the app package name.
        fullName = context.getPackageName() + name;
    } else if (name.indexOf('.') >= 0) {
        // 这里我们指定了全名
        // Fully qualified package name.
        fullName = name;
    } else {
        // Assume stock behavior in this package (if we have one)
        fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
                ? (WIDGET_PACKAGE_NAME + '.' + name)
                : name;
    }

    try {
        Map<String, Constructor<Behavior>> constructors = sConstructors.get();
        if (constructors == null) {
            constructors = new HashMap<>();
            sConstructors.set(constructors);
        }
        Constructor<Behavior> c = constructors.get(fullName);
        // 这里利用反射去实例化了指定的Behavior
        // 并且值得注意到是,这里指定了构造的参数类型
        // 也就是说我们在自定义Behavior的时候,必须要有这种类型的构造方法
        if (c == null) {
            final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
                    context.getClassLoader());
            c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
            c.setAccessible(true);
            constructors.put(fullName, c);
        }
        return c.newInstance(context, attrs);
    } catch (Exception e) {
        throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
    }
}

上面的代码很容易理解,就是利用反射机制去实例化了Behavior,调用的是两个参数的那个构造方法,这也就是我们在自定义Behavior的时候为什么一定要去重写,去SwipeDismissBehavior源码中找果然没有找到这个个构造方法,这样的话只能通过Java代码的方式设置了,很不方便。于是我自定义了一个Behavior继承自SwipeDismissBehavior。添加了一个相应的构造方法便于在xml inflat的时候反射调用。结果可以正常显示了。

public class MySwipeDismissBehavior extends SwipeDismissBehavior {
    public MySwipeDismissBehavior(Context context, AttributeSet attrs) {
    }

}

2.还有一点需要注意,由于Behavior是CoordinatorLayout特有的,保存在CoordinatorLayout.LayoutParamer中,所以要通过这种方式实现侧滑删除,只能用CoordinatorLayout做布局,且需要删除的view为CoordinatorLayout的第一级子VIew。
关于Behavior的深入理解,我将单独再写一篇博客。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值