目錄
案例效果
Scroller基本使用
其實Scroller的使用非常簡單總共就兩步玛迄,這里為了方便為下面的滑動刪除案例做準備我們就先實現(xiàn)一個類似的效果喜命,這里我是通過繼承LinearLayout實現(xiàn)的森缠,代碼如下:
public class ScrollUserLayout extends LinearLayout {
private Scroller mScroller;
public ScrollUserLayout(Context context) {
this(context,null);
}
public ScrollUserLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ScrollUserLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScroller = new Scroller(getContext());
}
/**
* 開始滑動
* @param distance 滑動的距離(注意這里不是坐標)
*/
public void startScroll(int distance){
mScroller.startScroll(0,0,distance,0);
}
/**
* 恢復(fù)初態(tài)
*/
public void reset(){
mScroller.startScroll(getScrollX(),0,-getScrollX(),0);
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
}
invalidate();
}
}
如上面代碼所示,整個邏輯非常簡單爪瓜,接下來我就解釋下使用Scroller的方法:
1.startScroll
mScroller.startScroll(0,0,distance,0);
這里需要傳四個參數(shù):
第一個參數(shù):為X軸方向的開始執(zhí)行滾動的位置
第二個參數(shù):為Y軸方向的開始執(zhí)行滾動的位置
第三個參數(shù):為X軸方向滑動的距離(注意不是位置坐標)
第四個參數(shù):為Y軸方向滑動的距離(注意不是位置坐標)
2.computeScroll
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
}
invalidate();
}
這個方法不是Scroller的方法而是View的方法瓣窄,這個方法是計算滾動時的一個回調(diào),其中Scroller的computeScrollOffset方法是用來判斷是否還在計算滾動索昂,如果還在計算建车,那么就需要調(diào)用View的scrollTo方法將當前控件滾動到Scroller計算的位置,然后在最后需要調(diào)用View的invalidate()方法來刷新控件椒惨。
而這個簡單的控件的使用也是比較簡單的缤至,布局文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt_open"
android:text="展開"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/bt_close"
android:text="收回"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.example.scrolldeletelayout.ScrollUserLayout
android:id="@+id/sul"
android:layout_width="match_parent"
android:layout_height="100dp">
<View
android:background="#ff00ff"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:id="@+id/v_right"
android:background="#ff0000"
android:layout_width="100dp"
android:layout_height="match_parent"/>
</com.example.scrolldeletelayout.ScrollUserLayout>
</LinearLayout>
Activity中的代碼如下:
public class TestActivity extends AppCompatActivity {
private View vRight;
private Button btOpen;
private Button btClose;
private ScrollUserLayout sul;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testactivity);
vRight = (View) findViewById(R.id.v_right);
btOpen = (Button) findViewById(R.id.bt_open);
btClose = (Button) findViewById(R.id.bt_close);
sul = (ScrollUserLayout) findViewById(R.id.sul);
btOpen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
sul.startScroll(vRight.getMeasuredWidth());
}
});
btClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
sul.reset();
}
});
}
}
運行的效果如下:
實現(xiàn)列表滑動刪除
接下來我們就以文章開頭效果圖展示的滑動刪除案例來熟悉Scroller的使用方法,這里效果的核心其實就是上面所說的知識康谆,而需要添加的邏輯就是對onTouchEvent事件的處理领斥,和在onLayout方法中獲取滑動的最大偏移,如下所示:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
firstTouchXTemp = event.getX();
firstTouchX = event.getX();
firstTouchY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
//計算出橫向和縱向滑動的距離沃暗,用來判斷是想滑動列表還是想拉出右側(cè)刪除按鈕
float xLen = Math.abs(event.getX() - firstTouchX);
float yLen = Math.abs(event.getY() - firstTouchY);
if(xLen > yLen){
//當確定了是橫向滑動時就請求父控件不攔截事件月洛,讓觸摸事件給本控件
getParent().requestDisallowInterceptTouchEvent(true);
float moveX = firstTouchXTemp - event.getX();
firstTouchXTemp = event.getX();
//計算控件之前的X軸滾動的距離和當前滑動的距離之和
float totalX = getScrollX() + moveX;
if(totalX > mMaxXOffset){
scrollTo(mMaxXOffset,0);
}else if(totalX < 0) {
scrollTo(0,0);
}else {
scrollBy((int) moveX,0);
}
}
break;
case MotionEvent.ACTION_UP:
//抬起手指的時候自動展開或收起
int scrollX = getScrollX();
if(scrollX > mMaxXOffset / 2){
//當滑動的距離大于右邊布局的一半時就展開
mScroller.startScroll(getScrollX(),0,mMaxXOffset - getScrollX(),0);
}else {
//當滑動的距離小于右邊布局的一半時就縮回
mScroller.startScroll(getScrollX(),0,-getScrollX(),0);
}
break;
}
return true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(getChildCount() > 1){
//找出刪除按鈕
deleteView = getChildAt(1);
setMaxXOffset(deleteView.getMeasuredWidth());
}
}
這里的邏輯其實也是比較簡單的,大家可以通過看下面的圖來理解
實現(xiàn)了以上邏輯之后描睦,基本上就完成了我們需要的效果了
但是我們我們發(fā)現(xiàn)膊存,會同時有很多的條目可以展開顯示刪除按鈕导而,因此我們需要優(yōu)化下忱叭,同一時間只能一個條目可以滑出刪除按鈕隔崎,也就是說我們需要在當前條目滑動出刪除按鈕的時候?qū)⑸弦粋€已經(jīng)滑出刪除按鈕的條目恢復(fù)原狀(即如文章開頭展示的效果那樣),因此我們需要在控件滑出刪除按鈕的時候加一個回調(diào)事件韵丑,用來通知上一個條目恢復(fù)原狀爵卒,代碼如下:
/**
* 當右邊隱藏的控件顯示出來的時候的回調(diào)
*/
public interface OnRightShowListener{
void onRightShow(ScrollDeleteLayout scrollDeleteLayout);
}
private OnRightShowListener onRightShowListener;
public OnRightShowListener getOnRightShowListener() {
return onRightShowListener;
}
public void setOnRightShowListener(OnRightShowListener onRightShowListener) {
this.onRightShowListener = onRightShowListener;
}
在onTouchEvent的MotionEvent.ACTION_UP中加入回調(diào)事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
//抬起手指的時候自動展開或收起
int scrollX = getScrollX();
if(scrollX > mMaxXOffset / 2){
//當滑動的距離大于右邊布局的一半時就展開
mScroller.startScroll(getScrollX(),0,mMaxXOffset - getScrollX(),0);
//加入滑出刪除按鈕的回調(diào)事件
if(onRightShowListener != null){
onRightShowListener.onRightShow(this);
}
}else {
//當滑動的距離小于右邊布局的一半時就縮回
mScroller.startScroll(getScrollX(),0,-getScrollX(),0);
}
break;
}
return true;
}
然后給適配器中的ScrollDeleteLayout設(shè)置OnRightShowListener事件回調(diào),然后在回調(diào)方法中做如下處理(其中preSdl為全局變量用來存儲當前滑開的ScrollDeleteLayout):
scrollDeleteLayout.setOnRightShowListener(new ScrollDeleteLayout.OnRightShowListener() {
@Override
public void onRightShow(ScrollDeleteLayout sdl) {
//展開當前的時候撵彻,復(fù)原上一個展開的钓株,如果是同一個的話就不復(fù)原了
if(preSdl != null && preSdl != sdl){
preSdl.reset(true);
}
preSdl = sdl;
}
});
加上回調(diào)之后的效果如下:
案例源碼
其中更多的細節(jié)可以看下案例源碼:https://gitee.com/itfitness/scroll-delete-layout