美文网首页AndroidAndroidAndroid技术
[android]手把手通过一个类实现侧滑退出activity功

[android]手把手通过一个类实现侧滑退出activity功

作者: 路人葵 | 来源:发表于2016-07-08 15:09 被阅读3349次

    1、概述#

    *本文代码 非原创 来自于 一个 叫做NBAPlus的开源代码中.https://github.com/SilenceDut/NBAPlus 有兴趣的小伙伴们可以好好去了解下,推荐下,真™可以的~

    2、简单描述下,所谓侧滑退出功能便是手指在界面左边或者右边滑动可以执行关闭界面的方法。换咱程序员的说话就是特么的:滑动 activity执行finish方法。


    效果图:#

    侧滑退出.gif

    2、主要实现方法#

    通过ViewDragHelper来检测到屏幕侧滑,然后通过内置接口传递给acitivity触发了侧滑事件,通知其关闭。

    额,就是这么简单明了的一句概况了。

    来来来代码代码看代码~喵了个巴拉的,具体细节看代码。因为感觉写太多细节的话,我自己都懒得去看。(咳咳……不是我懒不是我懒……嗯的,就是这样。)

    3、代码#

    1、实现侧滑删除,这里的方法是先要创建一个监听侧滑的自定义布局.####

    
    public class SwipeBackLayout extends FrameLayout {  
    
         //自定义控件 必备俩个构造函数
         public SwipeBackLayout(Context context) {
            this(context, null);//引用俩个参数的构造方法,目的是将三个构造方法连接起来.
         }
         public SwipeBackLayout(Context context, AttributeSet attrs) {     
            this(context, attrs, 0); //引用三个参数的构造方法
         }  
          public SwipeBackLayout(Context context, AttributeSet attrs, int  defStyleAttr) {     
            super(context, attrs, defStyleAttr); 
            init();//写个初始化方法,等下在里面实现 viewDragHelper,已经一些初始化操作
         }
    }
    

    2、哎哎哎,憋打我,容我在墨迹下,咱分析下要用到的属性。。。####

    • 1、首先TAG属性,好吧,其实并没有什么ruan用。打印Log用的。
      private static final String TAG = "SwipeBackLayout" ;
    • 2、都说ViewDragHelper实现的了,所以ViewDraghelper要有吧?总要知道滑动哪个view吧?view的宽度也得测量出来吧?是不是关闭也得知道吧?竟然要通知Activity那回调函数得来一个吧?侧滑得有阴影吧?透明度得控制下吧?……
       //于是乎……就有了这么一丢丢的属性……
       private ViewDragHelper mViewDragHelper;
       private View mContentView;
       private int mContentWidth;
       private int mMoveLeft;
       private boolean isClose = false;
       private boolean isEdgeDrag=false;
       private CallBack mCallBack;//自定义内部的回调函数,下面写
       private Drawable mShadowLeft;
       private static final int FULL_ALPHA = 255;
       private static final int DEFAULT_SCRIM_COLOR = 0x99000000;
       private int mScrimColor = DEFAULT_SCRIM_COLOR;
       private float mScrimOpacity;
       private float mScrollPercent;
       private Rect mTmpRect = new Rect();
    
    • 3、上面的属性中有个回调接口,我们这个接口主要就是通知acitivity什么时候关闭咯,那一个关闭的方法就ok了好的就不偷懒了,万一有和我一样的萌新看呢,贴上个代码
    //界面移出屏幕时接口回调
    public interface CallBack { 
       void onFinish();//这个就可以直接用了咯,然后在acitiviy中实例化接口
    }
    //设置回调接口,提供给activity实现接口
    public void setCallBack(CallBack callBack) {
        mCallBack = callBack;
    }
    

    3、属性既然定义好了,咱就把刚init()中的方法内容贴出来吧。就是在这货里面实现的viewGragHelper的!####

    //初始化方法private  void init(){ 
    
       //ViewDragHelper 需要用到的回调函数有点多哈,这里不介绍了。哈。那个啥大家可以多研究下,百度上资料也是千篇一律的。
       mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {   
             //返回true表示可以拖动     
             @Override     
             public boolean tryCaptureView(View child, int pointerId) {   
                 return child == mContentView;//如果child==mContentView,返回true,也就是说mContentView可以移动    
             }        
    
              //记录值的变化      
              @Override       
              public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {          
                  //记录左边的值的变化,因为我们实现的是往右滑动,所以只记录左边的值就可以了         
                  mScrollPercent = Math.abs((float) left/ (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));
                  mMoveLeft = left; 
                  if (isClose && (left == mContentWidth)) {
                  //如果当前状态是关闭状态且左边的值等于滑动的View的宽度,                
                  //也就是说当前的界面已经滑出屏幕,就回调finish方法,通知activity可以finish了             
                  mCallBack.onFinish();      
                  }
              }
    
              //手指松开会触发这个方法,做复位操作就在此方法中实现      
              @Override   
              public void onViewReleased(View releasedChild, float xvel, float yvel) {           
                       //一定得重写computeScroll()方法,不然没有效果          
                       //如果移动的距离大于或等于当前界面的1/10,则触发关闭     
                       if (mMoveLeft >= (mContentWidth / 10)) {       
                                 isClose = true;  //设置滑动的View移动位置,即然当前的界面滑出屏幕                
                                 mViewDragHelper.settleCapturedViewAt(mContentWidth, releasedChild.getTop());
                        } else {   
                                //设置滑动的View移动位置,即恢复原来的位置       
                                mViewDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
                        }            
                      //通知重绘界面       
                       invalidate();  
          }     
      
               //重新定位水平移动的位置     
               @Override      
               public int clampViewPositionHorizontal(View child, int left, int dx) {            
                //水平移动距离的范围是0~当前界面的宽度,如果left小于0直接返回0,            
                // 如果大于当前界面的宽度直接返回当前界面宽度  
                //也就是控制当前界面只能往右移动         
                   return Math.min(mContentWidth, Math.max(left, 0));       
               }      
      
                //设置水平拖动的距离   
                @Override   
                public int getViewHorizontalDragRange(View child) {  
                      //因为我们移动的是整个界面,所以直接返回整个界面的宽度就可以了          
                      return mContentWidth;      
               }      
     
                @Override  
                 public void onEdgeTouched(int edgeFlags, int pointerId) {            super.onEdgeTouched(edgeFlags, pointerId);  
                           isEdgeDrag = true;   
                  } 
                });    
                 mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);      
                 setShadow();//设置侧滑的边框,这个方法的代码下面写哈
    
    }
    
    

    4、init()方法已经写好了,里面有几个init要用到的方法贴出来吧####

    • 1、第一个重写的computeScroll方法,要不然onViewReleased会没效果。
    @Override
    public void computeScroll() {  
      super.computeScroll();    
      mScrimOpacity=1-mScrollPercent;
        //一定要做这个操作,否则onViewReleased不起作用   
      if (mViewDragHelper!=null&&mViewDragHelper.continueSettling(true)) {      
       invalidate();  
      }
    }
    
    
    • 2、然后就是setShadow()设置阴影的方法,侧滑时哪条灰不溜秋的线。为啥要?美呀! --图片资源我放在res目录下的mipmap中了

    图片:

    shadow_left.png
    public void setShadow() { 
          mShadowLeft=getResources().getDrawable(R.mipmap.shadow_left);    
          invalidate();
    }
    
    • 3、写了这么多,总要获取咱要滑动的那个布局吧。也就是上面咱定义的那个mContentView这个属性.
    @Override
    protected void onFinishInflate() {  
      super.onFinishInflate();   
      //SwipeBackFrameLayout的子View有且只有一个,否则抛异常    
      if (getChildCount() != 1) {    
        throw new IllegalStateException("SwipeBackFrameLayout must host one child.");
        }
        //取得当前布局的第一个子View,也是唯一一个子View
        //也就是activity的主要布局
        mContentView = getChildAt(0);
    }
    
    
    • 4、我们在到onLayout()中测量下mContentView宽度.
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    
       super.onLayout(changed, left, top, right, bottom);    //获取当前界面宽度 
       mContentWidth = mContentView.getWidth();
    }
    
    • 5、啊哟我去,还有一个很重要的步骤!脑抽的我没把触摸事件给viewDragHelper,如果没触摸事件还监听侧滑个……P呀.
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
       //把事件传递给
       ViewDragHelper 
       return mViewDragHelper.shouldInterceptTouchEvent(ev);
     }
      @Override
      public boolean onTouchEvent(MotionEvent event) {  
         //把事件传递给ViewDragHelper    
          mViewDragHelper.processTouchEvent(event);  
          invalidate();   
        return true;
      }
    
    • 6、最后设置了半天,还没画效果呀。画一下画一下,啥阴影呀,透明度呀……
    //画一个子项 ,到时候把acitivity的主题设置下就可以看到下面的activity了
    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {    
          final boolean drawContent = child == mContentView;   
          boolean ret = super.drawChild(canvas, child, drawingTime);  
          if (drawContent && mViewDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {      
                drawShadow(canvas, child);  
                drawScrim(canvas, child); 
           }  
           return ret;
     }
       //这个是那个阴影线
       private void drawShadow(Canvas canvas, View child) {   
                 final Rect childRect = mTmpRect;    
                 child.getHitRect(childRect);    
                 mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top,childRect.left, childRect.bottom);
                 mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA));    mShadowLeft.draw(canvas);
     }
        //这个就是画那个透明渐变出来的帷幕,还真™不知道怎么形容
        private void drawScrim(Canvas canvas, View child) {  
                 final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;    final int alpha = (int) (baseAlpha * mScrimOpacity);
                 final int color = alpha << 24 | (mScrimColor & 0xffffff);    
                 canvas.clipRect(0, 0, child.getLeft(), getHeight());    
                 canvas.drawColor(color);
     }
    
    • 7、没7了。。。到了这里 自定义的SwipeDragLayout就写完了~~~可以用了哦!

    4、 使用:三步走

    • 1、先把activity变成主题模式变透明的哦
          //设置下theme属性就好啦
          <activity 
              android:name=".BActivity"  
              android:theme="@style/AppTheme.TransparentActivity"    />```
    
    >>* 2、需要侧滑退出的`activity`的布局文件中嵌套上咱得自定义`SwipeBackLayout`.
    

    <?xml version="1.0" encoding="utf-8"?>
    <com.sunflower812.SwipeBackLayout
    android:id="@+id/swipe_layout_two"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#00eeaa" />
    </com.sunflower812.SwipeBackLayout>

    * 3、Ok!!!最后一步是什么呢~尼玛就是应用了呗。在`activity`中得到侧滑退出布局,设置他的回调接口.
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b); //找到控件
    //设置回调,然后就没然后了~~~
    SwipeBackLayout swipeBackLayoutTwo = (SwipeBackLayout) findViewById(R.id.swipe_layout_two);
    swipeBackLayoutTwo.setCallBack(new SwipeBackLayout.CallBack() {
    @Override
    public void onFinish() {
    finish();
    }
    });
    }

    * 4、哦!对了。很重要的就是。。自己写个页面跳转过来吧~调转到设置了滑动退出的这个页面。然后效果就实现了,侧滑退出!
    
    >#结尾:#
    * 附带DEMO代码:[Sunflower812博客--侧滑退出activity测试代码](http://download.csdn.net/detail/qq_33456552/9570882)
    

    路人葵:希望能帮助到有需求写这个功能的小伙伴们~~~

    相关文章

      网友评论

      • 翱翔IT:设置透明主题后若是使用SwipRefreshLayout嵌套ListView布局中有多图展示时(图片中有透明背景的图标)多滑动几次有可能出现图片部分穿透问题导致能看见前一级页面
      • 63903dec45f2:求解答~急用,时后自行补脑
      • 63903dec45f2:这是全屏侧滑~~怎么改只从左边拖动才能滑动?手指在中间滑是滑不动的
      • 3484cf783db0:这种方案activity里面有webview也有点小问题
        路人葵:@LanceWu_Amoy 能和我说下,问题么?我都想试下怎么解决。
      • hackware:这种方案不好,界面多了卡死了
        路人葵:@hackware 按我的理解的话,是 activity设置为透明的话,下面那个activity使处于绘制状态,可是可是下面的下面应该是不处于绘制状态了吧?…这个倒是没了解过。我试下多打开几个会不会卡。
        hackware: @路人葵 在4.4以下,没有不卡的方案,你想想看,当你把activity的主题设置为透明时,它下面的activity就是部分可见的,也就是说这些activity还在被绘制,多个window同时绘制负担相对重了很多。你试试开10个activity就知道了
        路人葵:@hackware 这样呀?也不是很了解,感觉挺好的,所以挺想知道为什么会卡死呀? 那怎样会不卡呢?

      本文标题:[android]手把手通过一个类实现侧滑退出activity功

      本文链接:https://www.haomeiwen.com/subject/zajljttx.html