Android App中實現向右滑動銷燬功能的要點解析

Android App中實現向右滑動銷燬功能的要點解析
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

今天給大家帶來一個向右滑動銷燬Activity的效果,Activtiy隨著手指的移動而移動,該效果在Android應用中還是比較少見的,在IOS中就比較常見了,例如“網易新聞” ,”美食傑” , “淘寶”等應用採用此效果,而Android應用中“知乎”採用的也是這種滑動切換Activity的效果, 不過我發現“淘寶”並沒有隨著手勢的移動而移動,只是捕捉到滑動手勢,然後產生平滑切換介面的動畫效果,這個在Android中還是很好實現的,  網上很多滑動切換Activity的Demo貌似都是這種效果的吧,如果要實現類似“網易新聞”的隨手勢的滑動而滑動,似乎就要複雜一些了,我之前在IOS中看到”網易新聞”的這種效果就很感興趣,然後群裡也有朋友問我怎麼實現類似“知乎”這個應用的滑動切換的效果,我也特意去下了一個“知乎”,在之前的實現中我遇到了一些瓶頸,沒有實現出來就擱置了在那裡,今天無意中看到給Activity設定透明的背景,於是乎我恍然大悟,真是靈感來源於瞬間,不能強求啊,然後自己就將此效果實現了出來,給大家分享一下,希望給有此需求的你一點點幫助。
不知道大家對Scroller這個類以及View的scrollBy() 和scrollTo()的使用熟悉不?我之前介紹了Scroller類的滑動實現原理Android 帶你從原始碼的角度解析Scroller的滾動實現原理,在那裡面也介紹了scrollBy() 和scrollTo()方法,不明白的同學可以去看看,這對實現此效果有很大的幫助,瞭解scrollBy() 和scrollTo()的朋友應該知道,如果想對某個View(例如Button)就行滾動,我們直接呼叫該View(Button)的scrollBy()方法,並不是該View(Button)進行滾動,而是該View裡面的內容(Button上面的文字)進行滾動,所以我們假如要讓View整體滾動就需要對其View的父佈局呼叫scrollBy()方法,回到這篇文章來,假如我們想要對一個Activity進行滾動,我們就需求對這個Activity佈局檔案的頂層佈局的父佈局進行滾動
例如下面的XML佈局檔案


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:gravity="center" 
android:orientation="vertical" > 
</LinearLayout> 

如果我們對LinearLayout進行滾動,並不能實現我們想要的效果,而只能對LinearLayout裡面的內容或者說是子View進行滾動,所以我們需要獲取利用LinearLayout的getParent()方法獲取父佈局,其實Android系統會對我們的佈局檔案的最外層套一個FrameLayout,所以我們其實就是對FrameLayout進行滾動就行了
瞭解了實現的原理之後,我們就來編寫程式碼吧,首先新建一個android工程,取名SildingFinish
由於我們的需求可能不是在一個介面提供這個滑動切換的效果,所以我們應該將這部分滑動的邏輯抽取出來,我這裡就他寫成了一個擴充套件RelativeLayout的自定義佈局SildingFinishLayout,首先我們看其程式碼


package com.example.view; 
import android.content.Context; 
import android.util.AttributeSet; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.View.OnTouchListener; 
import android.view.ViewConfiguration; 
import android.view.ViewGroup; 
import android.widget.AbsListView; 
import android.widget.RelativeLayout; 
import android.widget.ScrollView; 
import android.widget.Scroller; 
/** 
* 自定義可以滑動的RelativeLayout, 類似於IOS的滑動刪除頁面效果,當我們要使用 
* 此功能的時候,需要將該Activity的頂層佈局設定為SildingFinishLayout, 
* 然後需要呼叫setTouchView()方法來設定需要滑動的View 
* 
* @author xiaanming 
* 
* @blog http://blog.csdn.net/xiaanming 
* 
*/ 
public class SildingFinishLayout extends RelativeLayout implements 
OnTouchListener { 
/** 
* SildingFinishLayout佈局的父佈局 
*/ 
private ViewGroup mParentView; 
/** 
* 處理滑動邏輯的View 
*/ 
private View touchView; 
/** 
* 滑動的最小距離 
*/ 
private int mTouchSlop; 
/** 
* 按下點的X座標 
*/ 
private int downX; 
/** 
* 按下點的Y座標 
*/ 
private int downY; 
/** 
* 臨時儲存X座標 
*/ 
private int tempX; 
/** 
* 滑動類 
*/ 
private Scroller mScroller; 
/** 
* SildingFinishLayout的寬度 
*/ 
private int viewWidth; 
/** 
* 記錄是否正在滑動 
*/ 
private boolean isSilding; 
private OnSildingFinishListener onSildingFinishListener; 
private boolean isFinish; 
public SildingFinishLayout(Context context, AttributeSet attrs) { 
this(context, attrs, 0); 
} 
public SildingFinishLayout(Context context, AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 
mScroller = new Scroller(context); 
} 
@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
super.onLayout(changed, l, t, r, b); 
if (changed) { 
// 獲取SildingFinishLayout所在佈局的父佈局 
mParentView = (ViewGroup) this.getParent(); 
viewWidth = this.getWidth(); 
} 
} 
/** 
* 設定OnSildingFinishListener, 在onSildingFinish()方法中finish Activity 
* 
* @param onSildingFinishListener 
*/ 
public void setOnSildingFinishListener( 
OnSildingFinishListener onSildingFinishListener) { 
this.onSildingFinishListener = onSildingFinishListener; 
} 
/** 
* 設定Touch的View 
* 
* @param touchView 
*/ 
public void setTouchView(View touchView) { 
this.touchView = touchView; 
touchView.setOnTouchListener(this); 
} 
public View getTouchView() { 
return touchView; 
} 
/** 
* 滾動出介面 
*/ 
private void scrollRight() { 
final int delta = (viewWidth   mParentView.getScrollX()); 
// 呼叫startScroll方法來設定一些滾動的引數,我們在computeScroll()方法中呼叫scrollTo來滾動item 
mScroller.startScroll(mParentView.getScrollX(), 0, -delta   1, 0, 
Math.abs(delta)); 
postInvalidate(); 
} 
/** 
* 滾動到起始位置 
*/ 
private void scrollOrigin() { 
int delta = mParentView.getScrollX(); 
mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, 
Math.abs(delta)); 
postInvalidate(); 
} 
/** 
* touch的View是否是AbsListView, 例如ListView, GridView等其子類 
* 
* @return 
*/ 
private boolean isTouchOnAbsListView() { 
return touchView instanceof AbsListView ? true : false; 
} 
/** 
* touch的view是否是ScrollView或者其子類 
* 
* @return 
*/ 
private boolean isTouchOnScrollView() { 
return touchView instanceof ScrollView ? true : false; 
} 
@Override 
public boolean onTouch(View v, MotionEvent event) { 
switch (event.getAction()) { 
case MotionEvent.ACTION_DOWN: 
downX = tempX = (int) event.getRawX(); 
downY = (int) event.getRawY(); 
break; 
case MotionEvent.ACTION_MOVE: 
int moveX = (int) event.getRawX(); 
int deltaX = tempX - moveX; 
tempX = moveX; 
if (Math.abs(moveX - downX) > mTouchSlop 
&& Math.abs((int) event.getRawY() - downY) < mTouchSlop) { 
isSilding = true; 
// 若touchView是AbsListView, 
// 則當手指滑動,取消item的點選事件,不然我們滑動也伴隨著item點選事件的發生 
if (isTouchOnAbsListView()) { 
MotionEvent cancelEvent = MotionEvent.obtain(event); 
cancelEvent 
.setAction(MotionEvent.ACTION_CANCEL 
| (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 
v.onTouchEvent(cancelEvent); 
} 
} 
if (moveX - downX >= 0 && isSilding) { 
mParentView.scrollBy(deltaX, 0); 
// 遮蔽在滑動過程中ListView ScrollView等自己的滑動事件 
if (isTouchOnScrollView() || isTouchOnAbsListView()) { 
return true; 
} 
} 
break; 
case MotionEvent.ACTION_UP: 
isSilding = false; 
if (mParentView.getScrollX() <= -viewWidth / 2) { 
isFinish = true; 
scrollRight(); 
} else { 
scrollOrigin(); 
isFinish = false; 
} 
break; 
} 
// 假如touch的view是AbsListView或者ScrollView 我們處理完上面自己的邏輯之後 
// 再交給AbsListView, ScrollView自己處理其自己的邏輯 
if (isTouchOnScrollView() || isTouchOnAbsListView()) { 
return v.onTouchEvent(event); 
} 
// 其他的情況直接返回true 
return true; 
} 
@Override 
public void computeScroll() { 
// 呼叫startScroll的時候scroller.computeScrollOffset()返回true, 
if (mScroller.computeScrollOffset()) { 
mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
postInvalidate(); 
if (mScroller.isFinished()) { 
if (onSildingFinishListener != null && isFinish) { 
onSildingFinishListener.onSildingFinish(); 
} 
} 
} 
} 
public interface OnSildingFinishListener { 
public void onSildingFinish(); 
} 
} 

我們在onLayout()方法中利用getParent()方法獲取該佈局的父佈局和獲取其控制元件的寬度,主要是為之後的實現做準備工作。
我們的滑動邏輯主要是利用View的scrollBy() 方法, scrollTo()方法和Scroller類來實現的,當手指拖動檢視的時候,我們監聽手指在螢幕上滑動的距離利用View的scrollBy() 方法使得View隨著手指的滑動而滑動,而當手指離開螢幕,我們在根據邏輯使用Scroller類startScroll()方法設定滑動的引數,然後再根據View的scrollTo進行滾動。
對於View的滑動,存在一些Touch事件消費的處理等問題,因此我們需要對View的整個Touch事件很熟悉 ,最主要的就是Activity裡面有一些ListView、 GridView、ScrollView等控制元件了, 假如我們Activity裡面存在ListView、GridView等控制元件的話,我們對Activity的最外層佈局進行滾動根本就無效果,因為Touch事件被ListView、GridView等控制元件消費了,所以Activity的最外層佈局根本得不到Touch事件,也就實現不了Touch邏輯了,所以為了解決此Touch事件問題我提供了setTouchView(View touchView) 方法,這個方法是將Touch事件動態的設定到到View上面,所以針對上面的問題,我們將OnTouchListener直接設定到ListView、GridView上面,這樣子就避免了Activity的最外層接受不到Touch事件的問題了

接下來看onTouch()方法
首先我們在ACTION_DOWN記錄按下點的X,Y座標
然後在ACTION_MOVE中判斷,如果我們在水平方向滑動的距離大於mTouchSlop並且在豎直方向滑動的距離小於mTouchSlop,表示Activity處於滑動狀態,我們判斷如果touchView是ListView、GridView或者其子類的時候,因為我們手指在ListView、GridView上面,伴隨著item的點選事件的發生,所以我們對touchView設定ACTION_CANCEL來取消item的點選事件,然後對該佈局的父佈局呼叫scrollBy()進行滾動,並且如果TouchView是AbsListView或者ScrollView直接返回true,來取消AbsListView或者ScrollView本身的ACTION_MOVE事件,最直觀的感受就是我們在滑動Activity的時候,禁止AbsListView或者ScrollView的上下滑動
最後在ACTION_UP中判斷如果手指滑動的距離大於控制元件長度的二分之一,表示將Activity滑出介面,否則滑動到起始位置,我們利用Scroller類的startScroll()方法設定好開始位置,滑動距離和時間,然後呼叫postInvalidate()重新整理介面,之後就到computeScroll()方法中,我們利用scrollTo()方法對該佈局的父佈局進行滾動,滾動結束之後,我們判斷介面是否滑出介面,如果是就呼叫OnSildingFinishListener介面的onSildingFinish()方法,所以只要在onSildingFinish()方法中finish介面就行了
整個滑動佈局的程式碼就是這個樣子,接下來我們就來使用了,主介面Activity只有三個按鈕,分別跳轉到普通佈局的Activity,有ListView的Activity和有ScrollView的Activity中


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:gravity="center" 
android:orientation="vertical" > 
<Button 
android:id="@ id/normal_activity" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:text="普通的Activity" /> 
<Button 
android:id="@ id/absListview_activity" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:text="有AbsListView的Activity" /> 
<Button 
android:id="@ id/scrollview_activity" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:text="有ScrollView的Activity" /> 
</LinearLayout> 

 
然後就是MainActivity的程式碼,根據ID例項化Button,然後為Button設定OnClickListener事件,不同的按鈕跳轉到不同的Activity,然後設定從右向左滑動的動畫,重寫onBackPressed()方法,當我們按下手機物理鍵盤的返回鍵,新增從左向右滑出的動畫


package com.example.slidingfinish; 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.view.Window; 
import android.widget.Button; 
import com.example.slidingfinish.R; 
public class MainActivity extends Activity implements OnClickListener { 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
requestWindowFeature(Window.FEATURE_NO_TITLE); 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main); 
Button mButtonNormal = (Button) findViewById(R.id.normal_activity); 
mButtonNormal.setOnClickListener(this); 
Button mButtonAbs = (Button) findViewById(R.id.absListview_activity); 
mButtonAbs.setOnClickListener(this); 
Button mButtonScroll = (Button) findViewById(R.id.scrollview_activity); 
mButtonScroll.setOnClickListener(this); 
} 
@Override 
public void onClick(View v) { 
Intent mIntent = null; 
switch (v.getId()) { 
case R.id.normal_activity: 
mIntent = new Intent(MainActivity.this, NormalActivity.class); 
break; 
case R.id.absListview_activity: 
mIntent = new Intent(MainActivity.this, AbsActivity.class); 
break; 
case R.id.scrollview_activity: 
mIntent = new Intent(MainActivity.this, ScrollActivity.class); 
break; 
} 
startActivity(mIntent); 
overridePendingTransition(R.anim.base_slide_right_in, R.anim.base_slide_remain); 
} 
//Press the back button in mobile phone 
@Override 
public void onBackPressed() { 
super.onBackPressed(); 
overridePendingTransition(0, R.anim.base_slide_right_out); 
} 
} 

在這裡我之貼出含有ListView的Activity的程式碼,先看佈局,我們自定義滑動佈局SildingFinishLayout應該放在XML的最頂層


<?xml version="1.0" encoding="UTF-8"?> 
<com.example.view.SildingFinishLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:tools="http://schemas.android.com/tools" 
android:id="@ id/sildingFinishLayout" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:background="#556677" > 
<ListView 
android:id="@ id/listView" 
android:cacheColorHint="@android:color/transparent" 
android:layout_width="match_parent" 
android:layout_height="match_parent" > 
</ListView> 
</com.example.view.SildingFinishLayout> 

package com.example.slidingfinish; 
import java.util.ArrayList; 
import java.util.List; 
import android.app.Activity; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.View; 
import android.view.Window; 
import android.widget.AdapterView; 
import android.widget.AdapterView.OnItemClickListener; 
import android.widget.ArrayAdapter; 
import android.widget.ListView; 
import com.example.slidingfinish.R; 
import com.example.view.SildingFinishLayout; 
import com.example.view.SildingFinishLayout.OnSildingFinishListener; 
public class AbsActivity extends Activity { 
private List<String> list = new ArrayList<String>(); 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
requestWindowFeature(Window.FEATURE_NO_TITLE); 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_abslistview); 
for (int i = 0; i <= 30; i  ) { 
list.add("測試資料"   i); 
} 
ListView mListView = (ListView) findViewById(R.id.listView); 
ArrayAdapter<String> adapter = new ArrayAdapter<String>( 
AbsActivity.this, android.R.layout.simple_list_item_1, list); 
mListView.setAdapter(adapter); 
SildingFinishLayout mSildingFinishLayout = (SildingFinishLayout) findViewById(R.id.sildingFinishLayout); 
mSildingFinishLayout 
.setOnSildingFinishListener(new OnSildingFinishListener() { 
@Override 
public void onSildingFinish() { 
AbsActivity.this.finish(); 
} 
}); 
// touchView要設定到ListView上面 
mSildingFinishLayout.setTouchView(mListView); 
mListView.setOnItemClickListener(new OnItemClickListener() { 
@Override 
public void onItemClick(AdapterView<?> parent, View view, 
int position, long id) { 
startActivity(new Intent(AbsActivity.this, NormalActivity.class)); 
overridePendingTransition(R.anim.base_slide_right_in, 
R.anim.base_slide_remain); 
} 
}); 
} 
// Press the back button in mobile phone 
@Override 
public void onBackPressed() { 
super.onBackPressed(); 
overridePendingTransition(0, R.anim.base_slide_right_out); 
} 
} 

利用ID找到SildingFinishLayout例項,利用setTouchView()方法設定touchView到ListView上面,然後呼叫setOnSildingFinishListener()設定OnSildingFinishListener,在onSildingFinish()中finish介面就可以啦。
在執行專案之前還有一個很重要的操作,也是之前我被卡到的問題,就是我們需要對Activity設定為透明,即設定主題android:theme=”@android:style/Theme.Translucent”


<activity 
android:name=".AbsActivity" 
android:theme="@android:style/Theme.Translucent" > 
</activity> 
<activity 
android:name=".NormalActivity" 
android:theme="@android:style/Theme.Translucent" > 
</activity> 
<activity 
android:name=".ScrollActivity" 
android:theme="@android:style/Theme.Translucent" > 
</activity> 

 
好了,現在我們可以執行專案看看效果啦

正是我們想要的效果,如果想要加入滑動切換介面的效果只需要三步就行了,首先將Activity佈局的最外層修改為SildingFinishLayout,然後在Activity裡面呼叫setTouchView()方法設定touchView,設定OnSildingFinishListener監聽在onSildingFinish()方法中finish介面,最後設定Activity的背景為透明(不是設定Activity佈局檔案的最頂層佈局背景顏色透明,這點要區分一下)是不是很方便呢?好了,今天的講解到這裡就結束了~

您可能感興趣的文章:

Android程式設計實現橫豎屏切換時不銷燬當前activity和鎖定螢幕的方法Android的Activity跳轉動畫各種效果整理android的activity跳轉到另一個activityandroid PopupWindow 和 Activity彈出視窗實現方式Android基礎之Fragment與Activity互動詳解Android Activity之間傳遞圖片(Bitmap)的方法Android筆記之:App應用之啟動介面SplashActivity的使用Activity透明/半透明效果的設定transparent(兩種實現方法)android獲取當前執行Activity名字的方法Android Activity切換(跳轉)時出現黑屏的解決方法 分享Android實現Activity介面切換新增動畫特效的方法詳解Android.activity銷燬流程的工作原理

相關文章

Android 開發 最新文章