最近在研究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的深入理解,我将单独再写一篇博客。