1、概述#
*本文代碼 非原創(chuàng) 來自于 一個 叫做NBAPlus的開源代碼中.https://github.com/SilenceDut/NBAPlus 有興趣的小伙伴們可以好好去了解下俺孙,推薦下常柄,真?可以的~
2棋嘲、簡單描述下荠锭,所謂側(cè)滑退出功能便是手指在界面左邊或者右邊滑動可以執(zhí)行關(guān)閉界面的方法堤魁。換咱程序員的說話就是特么的:滑動
activity
執(zhí)行finish
方法蜜暑。
效果圖:#
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中了
圖片:
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)
路人葵:希望能幫助到有需求寫這個功能的小伙伴們~~~