[android]手把手通過一個類實(shí)現(xiàn)側(cè)滑退出activity功能


1、概述#

*本文代碼 非原創(chuàng) 來自于 一個 叫做NBAPlus的開源代碼中.https://github.com/SilenceDut/NBAPlus 有興趣的小伙伴們可以好好去了解下俺孙,推薦下常柄,真?可以的~

2棋嘲、簡單描述下荠锭,所謂側(cè)滑退出功能便是手指在界面左邊或者右邊滑動可以執(zhí)行關(guān)閉界面的方法堤魁。換咱程序員的說話就是特么的:滑動 activity執(zhí)行finish方法蜜暑。


效果圖:#

側(cè)滑退出.gif

2铐姚、主要實(shí)現(xiàn)方法#

通過ViewDragHelper來檢測到屏幕側(cè)滑,然后通過內(nèi)置接口傳遞給acitivity觸發(fā)了側(cè)滑事件肛捍,通知其關(guān)閉隐绵。

額,就是這么簡單明了的一句概況了拙毫。

來來來代碼代碼看代碼~喵了個巴拉的依许,具體細(xì)節(jié)看代碼。因?yàn)楦杏X寫太多細(xì)節(jié)的話缀蹄,我自己都懶得去看峭跳。(咳咳……不是我懶不是我懶……嗯的,就是這樣缺前。)

3蛀醉、代碼#

1、實(shí)現(xiàn)側(cè)滑刪除衅码,這里的方法是先要創(chuàng)建一個監(jiān)聽側(cè)滑的自定義布局.####


public class SwipeBackLayout extends FrameLayout {  

     //自定義控件 必備倆個構(gòu)造函數(shù)
     public SwipeBackLayout(Context context) {
        this(context, null);//引用倆個參數(shù)的構(gòu)造方法拯刁,目的是將三個構(gòu)造方法連接起來.
     }
     public SwipeBackLayout(Context context, AttributeSet attrs) {     
        this(context, attrs, 0); //引用三個參數(shù)的構(gòu)造方法
     }  
      public SwipeBackLayout(Context context, AttributeSet attrs, int  defStyleAttr) {     
        super(context, attrs, defStyleAttr); 
        init();//寫個初始化方法,等下在里面實(shí)現(xiàn) viewDragHelper,已經(jīng)一些初始化操作
     }
}

2逝段、哎哎哎垛玻,憋打我,容我在墨跡下惹恃,咱分析下要用到的屬性夭谤。。巫糙。####

  • 1朗儒、首先TAG屬性,好吧,其實(shí)并沒有什么ruan用醉锄。打印Log用的乏悄。
    private static final String TAG = "SwipeBackLayout" ;
  • 2、都說ViewDragHelper實(shí)現(xiàn)的了恳不,所以ViewDraghelper要有吧檩小?總要知道滑動哪個view吧?view的寬度也得測量出來吧烟勋?是不是關(guān)閉也得知道吧规求?竟然要通知Activity那回調(diào)函數(shù)得來一個吧?側(cè)滑得有陰影吧卵惦?透明度得控制下吧阻肿?……
   //于是乎……就有了這么一丟丟的屬性……
   private ViewDragHelper mViewDragHelper;
   private View mContentView;
   private int mContentWidth;
   private int mMoveLeft;
   private boolean isClose = false;
   private boolean isEdgeDrag=false;
   private CallBack mCallBack;//自定義內(nèi)部的回調(diào)函數(shù),下面寫
   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沮尿、上面的屬性中有個回調(diào)接口丛塌,我們這個接口主要就是通知acitivity什么時(shí)候關(guān)閉咯,那一個關(guān)閉的方法就ok了好的就不偷懶了畜疾,萬一有和我一樣的萌新看呢,貼上個代碼
//界面移出屏幕時(shí)接口回調(diào)
public interface CallBack { 
   void onFinish();//這個就可以直接用了咯赴邻,然后在acitiviy中實(shí)例化接口
}
//設(shè)置回調(diào)接口,提供給activity實(shí)現(xiàn)接口
public void setCallBack(CallBack callBack) {
    mCallBack = callBack;
}

3啡捶、屬性既然定義好了姥敛,咱就把剛init()中的方法內(nèi)容貼出來吧。就是在這貨里面實(shí)現(xiàn)的viewGragHelper的届慈!####

//初始化方法private  void init(){ 

   //ViewDragHelper 需要用到的回調(diào)函數(shù)有點(diǎn)多哈徒溪,這里不介紹了。哈金顿。那個啥大家可以多研究下臊泌,百度上資料也是千篇一律的。
   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) {          
              //記錄左邊的值的變化,因?yàn)槲覀儗?shí)現(xiàn)的是往右滑動嫂拴,所以只記錄左邊的值就可以了         
              mScrollPercent = Math.abs((float) left/ (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));
              mMoveLeft = left; 
              if (isClose && (left == mContentWidth)) {
              //如果當(dāng)前狀態(tài)是關(guān)閉狀態(tài)且左邊的值等于滑動的View的寬度播揪,                
              //也就是說當(dāng)前的界面已經(jīng)滑出屏幕,就回調(diào)finish方法筒狠,通知activity可以finish了             
              mCallBack.onFinish();      
              }
          }

          //手指松開會觸發(fā)這個方法猪狈,做復(fù)位操作就在此方法中實(shí)現(xiàn)      
          @Override   
          public void onViewReleased(View releasedChild, float xvel, float yvel) {           
                   //一定得重寫computeScroll()方法,不然沒有效果          
                   //如果移動的距離大于或等于當(dāng)前界面的1/10辩恼,則觸發(fā)關(guān)閉     
                   if (mMoveLeft >= (mContentWidth / 10)) {       
                             isClose = true;  //設(shè)置滑動的View移動位置雇庙,即然當(dāng)前的界面滑出屏幕                
                             mViewDragHelper.settleCapturedViewAt(mContentWidth, releasedChild.getTop());
                    } else {   
                            //設(shè)置滑動的View移動位置谓形,即恢復(fù)原來的位置       
                            mViewDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
                    }            
                  //通知重繪界面       
                   invalidate();  
      }     
  
           //重新定位水平移動的位置     
           @Override      
           public int clampViewPositionHorizontal(View child, int left, int dx) {            
            //水平移動距離的范圍是0~當(dāng)前界面的寬度,如果left小于0直接返回0疆前,            
            // 如果大于當(dāng)前界面的寬度直接返回當(dāng)前界面寬度  
            //也就是控制當(dāng)前界面只能往右移動         
               return Math.min(mContentWidth, Math.max(left, 0));       
           }      
  
            //設(shè)置水平拖動的距離   
            @Override   
            public int getViewHorizontalDragRange(View child) {  
                  //因?yàn)槲覀円苿拥氖钦麄€界面寒跳,所以直接返回整個界面的寬度就可以了          
                  return mContentWidth;      
           }      
 
            @Override  
             public void onEdgeTouched(int edgeFlags, int pointerId) {            super.onEdgeTouched(edgeFlags, pointerId);  
                       isEdgeDrag = true;   
              } 
            });    
             mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);      
             setShadow();//設(shè)置側(cè)滑的邊框,這個方法的代碼下面寫哈

}

4竹椒、init()方法已經(jīng)寫好了童太,里面有幾個init要用到的方法貼出來吧####

  • 1、第一個重寫的computeScroll方法胸完,要不然onViewReleased會沒效果书释。
@Override
public void computeScroll() {  
  super.computeScroll();    
  mScrimOpacity=1-mScrollPercent;
    //一定要做這個操作,否則onViewReleased不起作用   
  if (mViewDragHelper!=null&&mViewDragHelper.continueSettling(true)) {      
   invalidate();  
  }
}

  • 2舶吗、然后就是setShadow()設(shè)置陰影的方法征冷,側(cè)滑時(shí)哪條灰不溜秋的線。為啥要誓琼?美呀! --圖片資源我放在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.");
    }
    //取得當(dāng)前布局的第一個子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);    //獲取當(dāng)前界面寬度 
   mContentWidth = mContentView.getWidth();
}
  • 5窃页、啊喲我去跺株,還有一個很重要的步驟!腦抽的我沒把觸摸事件給viewDragHelper脖卖,如果沒觸摸事件還監(jiān)聽側(cè)滑個……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乒省、最后設(shè)置了半天,還沒畫效果呀畦木。畫一下畫一下袖扛,啥陰影呀,透明度呀……
//畫一個子項(xiàng) 十籍,到時(shí)候把a(bǔ)citivity的主題設(shè)置下就可以看到下面的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變成主題模式變透明的哦
      //設(shè)置下theme屬性就好啦
      <activity 
          android:name=".BActivity"  
          android:theme="@style/AppTheme.TransparentActivity"    />```

>>* 2、需要側(cè)滑退出的`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@艏椤!陶耍!最后一步是什么呢~尼瑪就是應(yīng)用了唄奋蔚。在`activity`中得到側(cè)滑退出布局,設(shè)置他的回調(diào)接口.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b); //找到控件
//設(shè)置回調(diào),然后就沒然后了~~~
SwipeBackLayout swipeBackLayoutTwo = (SwipeBackLayout) findViewById(R.id.swipe_layout_two);
swipeBackLayoutTwo.setCallBack(new SwipeBackLayout.CallBack() {
@Override
public void onFinish() {
finish();
}
});
}

* 4烈钞、哦泊碑!對了。很重要的就是毯欣。馒过。自己寫個頁面跳轉(zhuǎn)過來吧~調(diào)轉(zhuǎn)到設(shè)置了滑動退出的這個頁面。然后效果就實(shí)現(xiàn)了酗钞,側(cè)滑退出腹忽!

>#結(jié)尾:#
* 附帶DEMO代碼:[Sunflower812博客--側(cè)滑退出activity測試代碼](http://download.csdn.net/detail/qq_33456552/9570882)

路人葵:希望能幫助到有需求寫這個功能的小伙伴們~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砚作,隨后出現(xiàn)的幾起案子窘奏,更是在濱河造成了極大的恐慌,老刑警劉巖葫录,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件着裹,死亡現(xiàn)場離奇詭異,居然都是意外死亡米同,警方通過查閱死者的電腦和手機(jī)骇扇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來面粮,“玉大人少孝,你說我怎么就攤上這事〉穑” “怎么了韭山?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冷溃。 經(jīng)常有香客問我钱磅,道長,這世上最難降的妖魔是什么似枕? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任盖淡,我火速辦了婚禮,結(jié)果婚禮上凿歼,老公的妹妹穿的比我還像新娘褪迟。我一直安慰自己冗恨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布味赃。 她就那樣靜靜地躺著掀抹,像睡著了一般心俗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上城榛,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音狠持,去河邊找鬼。 笑死喘垂,一個胖子當(dāng)著我的面吹牛甜刻,可吹牛的內(nèi)容都是我干的王污。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼矾柜!你這毒婦竟也來了阱驾?” 一聲冷哼從身側(cè)響起怪蔑,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤里覆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缆瓣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喧枷,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年弓坞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隧甚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡渡冻,死狀恐怖戚扳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情族吻,我是刑警寧澤帽借,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布珠增,位于F島的核電站,受9級特大地震影響砍艾,放射性物質(zhì)發(fā)生泄漏蒂教。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一脆荷、第九天 我趴在偏房一處隱蔽的房頂上張望凝垛。 院中可真熱鬧,春花似錦简烘、人聲如沸苔严。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽届氢。三九已至,卻和暖如春覆旭,著一層夾襖步出監(jiān)牢的瞬間退子,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工型将, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寂祥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓七兜,卻偏偏與公主長得像丸凭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子腕铸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容