這篇文章主要介紹 Android如何實(shí)現(xiàn)右滑動(dòng)切換Activity的效果,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)憑借在網(wǎng)站建設(shè)、網(wǎng)站推廣領(lǐng)域領(lǐng)先的技術(shù)能力和多年的行業(yè)經(jīng)驗(yàn),為客戶(hù)提供超值的營(yíng)銷(xiāo)型網(wǎng)站建設(shè)服務(wù),我們始終認(rèn)為:好的營(yíng)銷(xiāo)型網(wǎng)站就是好的業(yè)務(wù)員。我們已成功為企業(yè)單位、個(gè)人等客戶(hù)提供了成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)服務(wù),以良好的商業(yè)信譽(yù),完善的服務(wù)及深厚的技術(shù)力量處于同行領(lǐng)先地位。
帶來(lái)一個(gè)向右滑動(dòng)切換Activity的效果,Activtiy隨著手指的移動(dòng)而移動(dòng),該效果在Android應(yīng)用中還是比較少見(jiàn)的,在IOS中就比較常見(jiàn)了,例如“網(wǎng)易新聞” ,"美食杰" , "淘寶"等應(yīng)用采用此效果,而Android應(yīng)用中“知乎”采用的也是這種滑動(dòng)切換Activity的效果, 不過(guò)我發(fā)現(xiàn)“淘寶”并沒(méi)有隨著手勢(shì)的移動(dòng)而移動(dòng),只是捕捉到滑動(dòng)手勢(shì),然后產(chǎn)生平滑切換界面的動(dòng)畫(huà)效果,這個(gè)在Android中還是很好實(shí)現(xiàn)的, 網(wǎng)上很多滑動(dòng)切換Activity的Demo貌似都是這種效果的吧,如果要實(shí)現(xiàn)類(lèi)似“網(wǎng)易新聞”的隨手勢(shì)的滑動(dòng)而滑動(dòng),似乎就要復(fù)雜一些了,我之前在IOS中看到"網(wǎng)易新聞"的這種效果就很感興趣,然后群里也有朋友問(wèn)我怎么實(shí)現(xiàn)類(lèi)似“知乎”這個(gè)應(yīng)用的滑動(dòng)切換的效果,我也特意去下了一個(gè)“知乎”,在之前的實(shí)現(xiàn)中我遇到了一些瓶頸,沒(méi)有實(shí)現(xiàn)出來(lái)就擱置了在那里,今天無(wú)意中看到給Activity設(shè)置透明的背景,于是乎我恍然大悟,真是靈感來(lái)源于瞬間,不能強(qiáng)求啊,然后自己就將此效果實(shí)現(xiàn)了出來(lái),給大家分享一下,希望給有此需求的你一點(diǎn)點(diǎn)幫助。
不知道大家對(duì)Scroller這個(gè)類(lèi)以及View的scrollBy() 和scrollTo()的使用熟悉不?我之前介紹了Scroller類(lèi)的滑動(dòng)實(shí)現(xiàn)原理Android 帶你從源碼的角度解析Scroller的滾動(dòng)實(shí)現(xiàn)原理,在那里面也介紹了scrollBy() 和scrollTo()方法,不明白的同學(xué)可以去看看,這對(duì)實(shí)現(xiàn)此效果有很大的幫助,了解scrollBy() 和scrollTo()的朋友應(yīng)該知道,如果想對(duì)某個(gè)View(例如Button)就行滾動(dòng),我們直接調(diào)用該View(Button)的scrollBy()方法,并不是該View(Button)進(jìn)行滾動(dòng),而是該View里面的內(nèi)容(Button上面的文字)進(jìn)行滾動(dòng),所以我們假如要讓View整體滾動(dòng)就需要對(duì)其View的父布局調(diào)用scrollBy()方法,回到這篇文章來(lái),假如我們想要對(duì)一個(gè)Activity進(jìn)行滾動(dòng),我們就需求對(duì)這個(gè)Activity布局文件的頂層布局的父布局進(jìn)行滾動(dòng)
例如下面的XML布局文件
如果我們對(duì)LinearLayout進(jìn)行滾動(dòng),并不能實(shí)現(xiàn)我們想要的效果,而只能對(duì)LinearLayout里面的內(nèi)容或者說(shuō)是子View進(jìn)行滾動(dòng),所以我們需要獲取利用LinearLayout的getParent()方法獲取父布局,其實(shí)Android系統(tǒng)會(huì)對(duì)我們的布局文件的最外層套一個(gè)FrameLayout,所以我們其實(shí)就是對(duì)FrameLayout進(jìn)行滾動(dòng)就行了
了解了實(shí)現(xiàn)的原理之后,我們就來(lái)編寫(xiě)代碼吧,首先新建一個(gè)android工程,取名SildingFinish
由于我們的需求可能不是在一個(gè)界面提供這個(gè)滑動(dòng)切換的效果,所以我們應(yīng)該將這部分滑動(dòng)的邏輯抽取出來(lái),我這里就他寫(xiě)成了一個(gè)擴(kuò)展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; /** * 自定義可以滑動(dòng)的RelativeLayout, 類(lèi)似于IOS的滑動(dòng)刪除頁(yè)面效果,當(dāng)我們要使用 * 此功能的時(shí)候,需要將該Activity的頂層布局設(shè)置為SildingFinishLayout, * 然后需要調(diào)用setTouchView()方法來(lái)設(shè)置需要滑動(dòng)的View * * @author xiaanming * * @blog http://blog.csdn.net/xiaanming * */ public class SildingFinishLayout extends RelativeLayout implements OnTouchListener { /** * SildingFinishLayout布局的父布局 */ private ViewGroup mParentView; /** * 處理滑動(dòng)邏輯的View */ private View touchView; /** * 滑動(dòng)的最小距離 */ private int mTouchSlop; /** * 按下點(diǎn)的X坐標(biāo) */ private int downX; /** * 按下點(diǎn)的Y坐標(biāo) */ private int downY; /** * 臨時(shí)存儲(chǔ)X坐標(biāo) */ private int tempX; /** * 滑動(dòng)類(lèi) */ private Scroller mScroller; /** * SildingFinishLayout的寬度 */ private int viewWidth; /** * 記錄是否正在滑動(dòng) */ 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(); } } /** * 設(shè)置OnSildingFinishListener, 在onSildingFinish()方法中finish Activity * * @param onSildingFinishListener */ public void setOnSildingFinishListener( OnSildingFinishListener onSildingFinishListener) { this.onSildingFinishListener = onSildingFinishListener; } /** * 設(shè)置Touch的View * * @param touchView */ public void setTouchView(View touchView) { this.touchView = touchView; touchView.setOnTouchListener(this); } public View getTouchView() { return touchView; } /** * 滾動(dòng)出界面 */ private void scrollRight() { final int delta = (viewWidth + mParentView.getScrollX()); // 調(diào)用startScroll方法來(lái)設(shè)置一些滾動(dòng)的參數(shù),我們?cè)赾omputeScroll()方法中調(diào)用scrollTo來(lái)滾動(dòng)item mScroller.startScroll(mParentView.getScrollX(), 0, -delta + 1, 0, Math.abs(delta)); postInvalidate(); } /** * 滾動(dòng)到起始位置 */ private void scrollOrigin() { int delta = mParentView.getScrollX(); mScroller.startScroll(mParentView.getScrollX(), 0, -delta, 0, Math.abs(delta)); postInvalidate(); } /** * touch的View是否是AbsListView, 例如ListView, GridView等其子類(lèi) * * @return */ private boolean isTouchOnAbsListView() { return touchView instanceof AbsListView ? true : false; } /** * touch的view是否是ScrollView或者其子類(lèi) * * @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, // 則當(dāng)手指滑動(dòng),取消item的點(diǎn)擊事件,不然我們滑動(dòng)也伴隨著item點(diǎn)擊事件的發(fā)生 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); // 屏蔽在滑動(dòng)過(guò)程中ListView ScrollView等自己的滑動(dòng)事件 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() { // 調(diào)用startScroll的時(shí)候scroller.computeScrollOffset()返回true, if (mScroller.computeScrollOffset()) { mParentView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); if (mScroller.isFinished()) { if (onSildingFinishListener != null && isFinish) { onSildingFinishListener.onSildingFinish(); }else{ //如果沒(méi)有設(shè)置回調(diào)接口, 直接劃到起始位置 scrollOrigin(); } } } } public interface OnSildingFinishListener { public void onSildingFinish(); } }
我們?cè)趏nLayout()方法中利用getParent()方法獲取該布局的父布局和獲取其控件的寬度,主要是為之后的實(shí)現(xiàn)做準(zhǔn)備工作。
我們的滑動(dòng)邏輯主要是利用View的scrollBy() 方法, scrollTo()方法和Scroller類(lèi)來(lái)實(shí)現(xiàn)的,當(dāng)手指拖動(dòng)視圖的時(shí)候,我們監(jiān)聽(tīng)手指在屏幕上滑動(dòng)的距離利用View的scrollBy() 方法使得View隨著手指的滑動(dòng)而滑動(dòng),而當(dāng)手指離開(kāi)屏幕,我們?cè)诟鶕?jù)邏輯使用Scroller類(lèi)startScroll()方法設(shè)置滑動(dòng)的參數(shù),然后再根據(jù)View的scrollTo進(jìn)行滾動(dòng)。
對(duì)于View的滑動(dòng),存在一些Touch事件消費(fèi)的處理等問(wèn)題,因此我們需要對(duì)View的整個(gè)Touch事件很熟悉 ,最主要的就是Activity里面有一些ListView、 GridView、ScrollView等控件了, 假如我們Activity里面存在ListView、GridView等控件的話,我們對(duì)Activity的最外層布局進(jìn)行滾動(dòng)根本就無(wú)效果,因?yàn)門(mén)ouch事件被ListView、GridView等控件消費(fèi)了,所以Activity的最外層布局根本得不到Touch事件,也就實(shí)現(xiàn)不了Touch邏輯了,所以為了解決此Touch事件問(wèn)題我提供了setTouchView(View touchView) 方法,這個(gè)方法是將Touch事件動(dòng)態(tài)的設(shè)置到到View上面,所以針對(duì)上面的問(wèn)題,我們將OnTouchListener直接設(shè)置到ListView、GridView上面,這樣子就避免了Activity的最外層接受不到Touch事件的問(wèn)題了
接下來(lái)看onTouch()方法
首先我們?cè)贏CTION_DOWN記錄按下點(diǎn)的X,Y坐標(biāo)
然后在ACTION_MOVE中判斷,如果我們?cè)谒椒较蚧瑒?dòng)的距離大于mTouchSlop并且在豎直方向滑動(dòng)的距離小于mTouchSlop,表示Activity處于滑動(dòng)狀態(tài),我們判斷如果touchView是ListView、GridView或者其子類(lèi)的時(shí)候,因?yàn)槲覀兪种冈贚istView、GridView上面,伴隨著item的點(diǎn)擊事件的發(fā)生,所以我們對(duì)touchView設(shè)置ACTION_CANCEL來(lái)取消item的點(diǎn)擊事件,然后對(duì)該布局的父布局調(diào)用scrollBy()進(jìn)行滾動(dòng),并且如果TouchView是AbsListView或者ScrollView直接返回true,來(lái)取消AbsListView或者ScrollView本身的ACTION_MOVE事件,最直觀的感受就是我們?cè)诨瑒?dòng)Activity的時(shí)候,禁止AbsListView或者ScrollView的上下滑動(dòng)
最后在ACTION_UP中判斷如果手指滑動(dòng)的距離大于控件長(zhǎng)度的二分之一,表示將Activity滑出界面,否則滑動(dòng)到起始位置,我們利用Scroller類(lèi)的startScroll()方法設(shè)置好開(kāi)始位置,滑動(dòng)距離和時(shí)間,然后調(diào)用postInvalidate()刷新界面,之后就到computeScroll()方法中,我們利用scrollTo()方法對(duì)該布局的父布局進(jìn)行滾動(dòng),滾動(dòng)結(jié)束之后,我們判斷界面是否滑出界面,如果是就調(diào)用OnSildingFinishListener接口的onSildingFinish()方法,所以只要在onSildingFinish()方法中finish界面就行了
整個(gè)滑動(dòng)布局的代碼就是這個(gè)樣子,接下來(lái)我們就來(lái)使用了,主界面Activity只有三個(gè)按鈕,分別跳轉(zhuǎn)到普通布局的Activity,有ListView的Activity和有ScrollView的Activity中
然后就是MainActivity的代碼,根據(jù)ID實(shí)例化Button,然后為Button設(shè)置OnClickListener事件,不同的按鈕跳轉(zhuǎn)到不同的Activity,然后設(shè)置從右向左滑動(dòng)的動(dòng)畫(huà),重寫(xiě)onBackPressed()方法,當(dāng)我們按下手機(jī)物理鍵盤(pán)的返回鍵,添加從左向右滑出的動(dòng)畫(huà)
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的代碼,先看布局,我們自定義滑動(dòng)布局SildingFinishLayout應(yīng)該放在XML的最頂層
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 Listlist = new ArrayList (); @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("測(cè)試數(shù)據(jù)" + i); } ListView mListView = (ListView) findViewById(R.id.listView); ArrayAdapter adapter = new ArrayAdapter ( 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要設(shè)置到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實(shí)例,利用setTouchView()方法設(shè)置touchView到ListView上面,然后調(diào)用setOnSildingFinishListener()設(shè)置OnSildingFinishListener,在onSildingFinish()中finish界面就可以啦。
在運(yùn)行項(xiàng)目之前還有一個(gè)很重要的操作,也是之前我被卡到的問(wèn)題,就是我們需要對(duì)Activity設(shè)置為透明,即設(shè)置主題android:theme="@android:style/Theme.Translucent"
好了,現(xiàn)在我們可以運(yùn)行項(xiàng)目看看效果啦
以上是“ Android如何實(shí)現(xiàn)右滑動(dòng)切換Activity的效果”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!