RecyclerView 是最佳选择!
第一,RecyclerView 是自带 Item View 回收和重用功能的,就不需要我们考虑这个问题了;
第二,RecyclerView 的布局方式是通过设置 LayoutManager 来实现的,这样就充分地把布局和 RecyclerView “解耦”开来了。而 LayoutManager 是可以通过自定义的方式来实现的。这恰恰是我们想要的(实现卡片层叠式)!!!
下面详细说一下:
1. RecyclerView
属于新增的控件,Android将RecyclerView定义在support库里。若要使用RecyclerView,第一步是要在build.gradle
中添加对应的依赖库。
在app/build.gradle
中的dependencies闭包
添加以下内容:
implementation 'com.android.support:recyclerview-v7:27.1.1'
由于RecyclerView
不是内置在系统SDK中,需要把其完整的包名路径写出来
<android.support.v7.widget.RecyclerView
2. 然后,每一个item,都要有一个类(java类文件)card.java和一个布局文件(card.xml),类中保存界面中的数据。
3. 新增适配器CardAdapter.java,里面有数据集合list,是activity传过来的,在adapter里定义了一个viewholder类,在它的构造函数中实现卡片上控件的获取,因为传入了一个view,就是用这个view实现的。这个viewholder的初始化在于adapter 重载了oncreateviewholder函数,返回这个holder就行。而view也就是在这时候直接获取的R.
4.如何与数据绑定呢?这里adapter重载了onBindViewHolder(ViewHolder holder, int position),在这里就可以实现与特定的数据设置了,例如
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
至此recyclerview设置完毕了。在哪里设置监听呢?在adapter里设置关于item的点击监听。
3. 那么如何实现滑动效果呢?卡片里是一个layout,卡片是如何排列的是另一种layout。实际上,卡片如何排列的全靠用代码写出来的,创建 CardLayoutManager
并继承自 RecyclerView.LayoutManager,在这里实现实现重叠排列,显示多少个,以及后续添加的效果,然后用recyclerview绑定它就可以了。添加子卡片完全是用addview实现的,比较方便,实际上,list是时常变化的,究竟选择那一个单词,实际上应该是在这里单独出一个类,用来选择list。
4. 紧接着,可以拖动它的话,需要设置一个
先定义一个监听器。主要用于监听卡片滑动事件,是一个interface,究竟是怎么用,看下面。
5. 用 ItemTouchHelper ,也是直接关联在控件上,而 ItemTouchHelper又关联了一个callback,在callback里面写触发函数,具体怎么回事呢,反正重写了函数,就会有拖动的效果。反正大致就这么样的,不同的功能,就添加到上面就可以了。
唉,算了,下面学习一下别的,但是今天真的没时间了。
itemDragListener是adapter里的一个变量,初始化的时候传进去了(是上下文。。。),adapter接了ItemMoveListener的接口
ItemTouchHelper是绑定在recyclerview上的,跟adapter差不多,本身activity里面就有一个private ItemTouchHelper mItemTouchHelper;是下面用的。
在adapter里有private ItemDragListener mItemDragListener;在里面也并没有发现有关于什么的代码。
反正添加代码就那么个意思,容易了。
看来还得实现自己的布局。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。没有任何实现代码。接口中所有的方法,都是抽象方法,抽象方法是用abstract定义的,但interface不用,它本来就是。接口中除了定义方法外,还可以定义成员变量,方法和属性默认都是public修饰,也可以使用protected,但不能用private ,所有的属性都是静态的常量,默认省略了static和final修饰符,属性的值必须实例化(初始化)。
在JAVA中,一个类无法继承自多个类,但是可以实现多个接口,使用关键字implements 多个接口之间使用“,”隔开 多个接口之间,没有先后顺序。
各种监听器listener比较喜欢用interface,里面的函数是on什么什么。本身activity继承了ItemDragListener,实现了onStartDrags,里面调用了mItemTouchHelper.startDrag(viewHolder);
adapter继承了ItemMoveListener,它的功能是侧滑删除和拖动交换位置。它用MyItemTouchHelperCallback实现初始化,这个类继承自ItemTouchHelper.Callback,里面决定了左右拖动或交换位置的具体代码。只要把这个helper给.attachToRecyclerView(recyclerView);就可以了。
不简单的是,这个callback里有一个ItemMoveListener的属性,该属性恰好是adapter继承的,因此填入了adapter。因为这个callback里面有onmove和onswipe,这里才是真正的系统调用,这时调用adapter里面的onitemmove什么的,因为adapter里面正好有数据,可以删除。因此,itemmovelistener是用来操作每一个项的。比较巧妙。
那么,不要这个抽象接口行不行呢?首先,item的move和swipe的触发是在helper的callback首先传进来的,因此callback完全可以用adapter的类实现而不用接口,毕竟接口只是实现通用性的工具。
但是从另外一个角度来说,callback里面的属性是mItemMoveListener,而不是adapter,毕竟也用不到adapter其他的函数,因此这样更清晰了。因此不要改为妙。
重新总结一下:
activity里声明了mItemTouchHelper
adapter里继承了ItemMoveListener
ItemTouchHelper的callback声明了ItemMoveListener,也就是adapter
然后callback用adapter初始化,ItemTouchHelper用callback初始化,然后再关联到recyclerview上,完成。
接下来看看drag listener,前面说到,是activity实现了这个接口,而实现的onStartDrags里只调用了mItemTouchHelper.startDrag(viewHolder);,如果你想自定义触摸view,那么就使用startDrag(ViewHolder)方法。
adapter中声明了一个mItemDragListener,好乱啊。。。在初始化的时候就用activity传入了。在继承的onBindViewHolder函数中使用了
public void onBindViewHolder(final ViewHolder holder, int position) {
RecyclerItem recyclerItem = mList.get(position); // 刚绑定的时候,设置数据
holder.textView.setText(recyclerItem.getText());
holder.textView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mItemDragListener.onStartDrags(holder);
return false;
}
});
}
可以看到,viewholder就是一个item的UI,在这里实现UI与数据的绑定,当然,还有点击函数的绑定。因此在这里使用了mItemDragListener.onStartDrags(holder);,转而执行activity的onStartDrags,进而执行mItemTouchHelper.startDrag(viewHolder);,说过了,如果你想自定义触摸view,那么就使用startDrag(ViewHolder)方法。现在的这个holder明显是整个item。可能说,传进去之后,就会传出一个onmove,在这个onmove里面实现list的删除,就是这样的。上面写着为图形拖动做准备。
成功了,然后卡片的大小,阴影,颜色,都是自己设定的。实现阴影,圆角,都是通过引用xml文件显示出来的,后面再做吧。
对,先做阴影什么的
设定圆角矩形,需要在res里面加上Android resourse file即可。
类似的例如这样:
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#ffffff" /> <stroke android:color="#20000000" android:width="1dp"/> <corners android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp" android:topLeftRadius="10dp" android:topRightRadius="10dp" /> </shape>
其中corners 中定义每边的圆角弧度。
solid为填充的颜色:半透明颜色:#10000000~#90000000 透明深度不一样。指定内部填充色
gradient:用以定义渐变色,可以定义两色渐变和三色渐变,及渐变样式
stroke:描边属性,可以定义描边的宽度,颜色,虚实线等
size:定义图形的大小
在这里
https://www.jianshu.com/p/b65e489093c7
在background引用即可。
突然感觉好乱啊。。。首先说,callback是一个类,它不是接口,也不会继承。它里面调用了list和adapter,但是callback里面怎么可能有?
mItemTouchHelper在CardLayoutManager就有。但是实际上没有用到它。
OnSwipeListener也就是在adapter里用了一下。
说实话,到现在这里已经完全乱了。
不管怎么样,现在成功了。。。
现在没有回调监听,下面的有些不成功。
感觉不错,但是现在已经完全OK了。
那你首先应该知道,这三个接口:DragListener,MoveListener和Swipe Listener各自有什么作用。
DragListener:
void onStartDrags(RecyclerView.ViewHolder viewHolder);
里面调用了ItemTouchHelper的startDrag(viewHolder)方法可以手动开启拖动效果,用来开启拖动动画(当拖动时)。
CardMoveListener:
boolean onItemMove(int fromPosition, int toPosition); boolean onItemRemove(RecyclerView.ViewHolder viewHolder);
看来实现了card的拖动与移出监听。
public interface OnSwipeListener<T> void onSwiping(RecyclerView.ViewHolder viewHolder, float ratio, int direction);卡片还在滑动时回调 void onSwiped(RecyclerView.ViewHolder viewHolder, T t, int direction);卡片完全滑出时回调 void onSwipedClear();所有的卡片全部滑出时回调
看来实现了卡片在各个状态时的回调,用于复位,删除等。大概就是利用DragListener开启拖动效果,然后用Swipe Listener监听各种状态,最后用CardMoveListener实现删除。
因此,Drag可以直接不用了。。。然后猜测利用swip触发函数实现的move触发。
那到底谁用了swipelistener呢?感觉没人用啊。。。
现在基本上都成功了。。。但是现在还没搞明白。
再串通一遍。它之所以用接口是为什么呢,是因为几个类之间需要相互调用,adapter就是处理数据的地方,callback就是监听的地方,在监听的地方调用adapter的数据处理函数,就是这样的,根本用不了接口。
但是从另一个角度说来,on什么什么是别人调用的,自己实现内容即可,对吧。怎么个设计方法呢?
怎么说呢,这接口就这样吧。名字还是很直白的。
那么adapter是主管数据的,那么helper和layout分别管什么呢?
其实layout掌管着显示和排列。在helper里判定滑动到哪里算是可以过去。在callback的固有继承的函数里去调用了几个on回调。
在onChildDraw上实现了判定。
因此感觉监听器意义不大了,毕竟没有谁用这个swipe接口,删掉吧,一点意义也没有了。
不是,它是用来判断是左划还是右划什么的,还是划完了,这些状态还是蛮重要的,继承给谁呢?继承给主activity就可以显示弹窗了是不是。它的作用是这个。