關於安卓開發實現側滑菜單效果

學習出處:http://blog.csdn.net/guolin_blog/article/details/8714621java

 

這裏不轉載內容了,按照本身理解寫一篇android

 

側滑菜單效果 就是手機版QQ的左側向右滑動出現菜單欄的那一種效果app

 

實現原理。在一個Activity的佈局中須要有兩部分,一個是菜單(menu)的佈局,一個是內容(content)的佈局。兩個佈局橫向排列,菜單佈局在左,內容佈局在右。初始化的時候將菜單佈局向左偏移,以致於可以徹底隱藏,這樣內容佈局就會徹底顯示在Activity中。而後經過監聽手指滑動事件,來改變菜單佈局的左偏移距離,從而控制菜單佈局的顯示和隱藏。ide

 

原理圖(學習做者畫的,非本菜鳥畫的。)以下:佈局

content是主界面  至關於手機QQ聊天的那個界面 學習

menu是側滑菜單,至關於顯示我的信息的那個界面 (不截圖了,由於QQ滑動縮小,本菜鳥作的滑動兩個界面大小都不變化)動畫

將菜單佈局的左偏移值改爲0時,效果圖以下:this

全部代碼由三部分組成spa

src文件下的     Main.java  .net

res/layout下的activity_main.xml  

AndroidManifest.xml

先來看佈局文件:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 2     xmlns:tools="http://schemas.android.com/tools"  
 3     android:layout_width="fill_parent"  
 4     android:layout_height="fill_parent"  
 5     android:orientation="horizontal"  
 6     tools:context=".Main" >  
 7   
 8     <LinearLayout  
 9         android:orientation="horizontal"   
10 //第一行在我學習的文章中是沒有的,可是本身不加就出錯,這是設置水平佈局的意思
11         android:id="@+id/menu"  
12         android:layout_width="fill_parent"  
13         android:layout_height="fill_parent"  
14         android:background="@drawable/right" > //添加背景圖片,爲了顯示方便。這是側滑界面。就是滑動出來的界面
15     </LinearLayout>  
16   
17     <LinearLayout  
18         android:orientation="horizontal"  
19         android:id="@+id/content"  
20         android:layout_width="fill_parent"  
21         android:layout_height="fill_parent" 
22         android:background="@drawable/main_picture"  //這是主界面,就是不滑動時顯示的界面 
23          >  
24     </LinearLayout>  
25   
26 </LinearLayout>  

這個佈局文件的最外層佈局是一個LinearLayout,排列方向是水平方向排列。這個LinearLayout下面嵌套了兩個子LinearLayout,分別就是菜單的佈局和內容的佈局。這裏爲了要讓佈局儘可能簡單,菜單佈局和內容佈局裏面沒有加入任何控件,只是給這兩個佈局各添加了一張背景圖片,這樣咱們能夠把注意力都集中在如何實現滑動菜單的效果上面,不用關內心面各類複雜的佈局了。

 

 

而後是主類 

Main.java

  1 package xqx;
  2 
  3 import com.example.xqx_lianxi.R;
  4 
  5 import android.app.Activity;
  6 import android.content.Context;
  7 import android.os.AsyncTask;
  8 import android.os.Bundle;
  9 import android.view.MotionEvent;
 10 import android.view.VelocityTracker;
 11 import android.view.View;
 12 import android.view.View.OnTouchListener;
 13 import android.view.WindowManager;
 14 import android.widget.LinearLayout;
 15 
 16 
 17 public class Main extends Activity implements OnTouchListener{
 18     /** 
 19      * 滾動顯示和隱藏menu時,手指滑動須要達到的速度。 
 20      */  
 21     public static final int SNAP_VELOCITY = 200;  
 22   
 23     /** 
 24      * 屏幕寬度值。 
 25      */  
 26     private int screenWidth;  
 27   
 28     /** 
 29      * menu最多能夠滑動到的左邊緣。值由menu佈局的寬度來定,marginLeft到達此值以後,不能再減小。 
 30      */  
 31     private int leftEdge;  
 32   
 33     /** 
 34      * menu最多能夠滑動到的右邊緣。值恆爲0,即marginLeft到達0以後,不能增長。 
 35      */  
 36     private int rightEdge = 0;  
 37   
 38     /** 
 39      * menu徹底顯示時,留給content的寬度值。 
 40      */  
 41     private int menuPadding = 80;  
 42   
 43     /** 
 44      * 主內容的佈局。 
 45      */  
 46     private View content;  
 47   
 48     /** 
 49      * menu的佈局。 
 50      */  
 51     private View menu;  
 52   
 53     /** 
 54      * menu佈局的參數,經過此參數來更改leftMargin的值。 
 55      */  
 56     private LinearLayout.LayoutParams menuParams;  
 57   
 58     /** 
 59      * 記錄手指按下時的橫座標。 
 60      */  
 61     private float xDown;  
 62   
 63     /** 
 64      * 記錄手指移動時的橫座標。 
 65      */  
 66     private float xMove;  
 67   
 68     /** 
 69      * 記錄手機擡起時的橫座標。 
 70      */  
 71     private float xUp;  
 72   
 73     /** 
 74      * menu當前是顯示仍是隱藏。只有徹底顯示或隱藏menu時纔會更改此值,滑動過程當中此值無效。 
 75      */  
 76     private boolean isMenuVisible;  
 77   
 78     /** 
 79      * 用於計算手指滑動的速度。 
 80      */  
 81     private VelocityTracker mVelocityTracker;
 82     @Override
 83     protected void onCreate(Bundle savedInstanceState) {
 84         // TODO Auto-generated method stub
 85         super.onCreate(savedInstanceState);
 86         setContentView(R.layout.activity_main);
 87         
 88         initValues();  
 89         content.setOnTouchListener(this);  
 90     }  
 91   
 92     /** 
 93      * 初始化一些關鍵性數據。包括獲取屏幕的寬度,給content佈局從新設置寬度,給menu佈局從新設置寬度和偏移距離等。 
 94      */  
 95     private void initValues() {  
 96         WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);  
 97         screenWidth = window.getDefaultDisplay().getWidth();  
 98         content = findViewById(R.id.content);  
 99         menu = findViewById(R.id.menu);  
100         menuParams = (LinearLayout.LayoutParams) menu.getLayoutParams();  
101         // 將menu的寬度設置爲屏幕寬度減去menuPadding  
102         menuParams.width = screenWidth - menuPadding;  
103         // 左邊緣的值賦值爲menu寬度的負數  
104         leftEdge = -menuParams.width;  
105         // menu的leftMargin設置爲左邊緣的值,這樣初始化時menu就變爲不可見  
106         menuParams.leftMargin = leftEdge;  
107         // 將content的寬度設置爲屏幕寬度  
108         content.getLayoutParams().width = screenWidth;  
109     }  
110   
111     @Override  
112     public boolean onTouch(View v, MotionEvent event) {  
113         createVelocityTracker(event);  
114         switch (event.getAction()) {  
115         case MotionEvent.ACTION_DOWN:  
116             // 手指按下時,記錄按下時的橫座標  
117             xDown = event.getRawX();  
118             break;  
119         case MotionEvent.ACTION_MOVE:  
120             // 手指移動時,對比按下時的橫座標,計算出移動的距離,來調整menu的leftMargin值,從而顯示和隱藏menu  
121             xMove = event.getRawX();  
122             int distanceX = (int) (xMove - xDown);  
123             if (isMenuVisible) {  
124                 menuParams.leftMargin = distanceX;  
125             } else {  
126                 menuParams.leftMargin = leftEdge + distanceX;  
127             }  
128             if (menuParams.leftMargin < leftEdge) {  
129                 menuParams.leftMargin = leftEdge;  
130             } else if (menuParams.leftMargin > rightEdge) {  
131                 menuParams.leftMargin = rightEdge;  
132             }  
133             menu.setLayoutParams(menuParams);  
134             break;  
135         case MotionEvent.ACTION_UP:  
136             // 手指擡起時,進行判斷當前手勢的意圖,從而決定是滾動到menu界面,仍是滾動到content界面  
137             xUp = event.getRawX();  
138             if (wantToShowMenu()) {  
139                 if (shouldScrollToMenu()) {  
140                     scrollToMenu();  
141                 } else {  
142                     scrollToContent();  
143                 }  
144             } else if (wantToShowContent()) {  
145                 if (shouldScrollToContent()) {  
146                     scrollToContent();  
147                 } else {  
148                     scrollToMenu();  
149                 }  
150             }  
151             recycleVelocityTracker();  
152             break;  
153         }  
154         return true;  
155     }  
156   
157     /** 
158      * 判斷當前手勢的意圖是否是想顯示content。若是手指移動的距離是負數,且當前menu是可見的,則認爲當前手勢是想要顯示content。 
159      *  
160      * @return 當前手勢想顯示content返回true,不然返回false。 
161      */  
162     private boolean wantToShowContent() {  
163         return xUp - xDown < 0 && isMenuVisible;  
164     }  
165   
166     /** 
167      * 判斷當前手勢的意圖是否是想顯示menu。若是手指移動的距離是正數,且當前menu是不可見的,則認爲當前手勢是想要顯示menu。 
168      *  
169      * @return 當前手勢想顯示menu返回true,不然返回false。 
170      */  
171     private boolean wantToShowMenu() {  
172         return xUp - xDown > 0 && !isMenuVisible;  
173     }  
174   
175     /** 
176      * 判斷是否應該滾動將menu展現出來。若是手指移動距離大於屏幕的1/2,或者手指移動速度大於SNAP_VELOCITY, 
177      * 就認爲應該滾動將menu展現出來。 
178      *  
179      * @return 若是應該滾動將menu展現出來返回true,不然返回false。 
180      */  
181     private boolean shouldScrollToMenu() {  
182         return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  
183     }  
184   
185     /** 
186      * 判斷是否應該滾動將content展現出來。若是手指移動距離加上menuPadding大於屏幕的1/2, 
187      * 或者手指移動速度大於SNAP_VELOCITY, 就認爲應該滾動將content展現出來。 
188      *  
189      * @return 若是應該滾動將content展現出來返回true,不然返回false。 
190      */  
191     private boolean shouldScrollToContent() {  
192         return xDown - xUp + menuPadding > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;  
193     }  
194   
195     /** 
196      * 將屏幕滾動到menu界面,滾動速度設定爲30. 
197      */  
198     private void scrollToMenu() {  
199         new ScrollTask().execute(30);  
200     }  
201   
202     /** 
203      * 將屏幕滾動到content界面,滾動速度設定爲-30. 
204      */  
205     private void scrollToContent() {  
206         new ScrollTask().execute(-30);  
207     }  
208   
209     /** 
210      * 建立VelocityTracker對象,並將觸摸content界面的滑動事件加入到VelocityTracker當中。 
211      *  
212      * @param event 
213      *            content界面的滑動事件 
214      */  
215     private void createVelocityTracker(MotionEvent event) {  
216         if (mVelocityTracker == null) {  
217             mVelocityTracker = VelocityTracker.obtain();  
218         }  
219         mVelocityTracker.addMovement(event);  
220     }  
221   
222     /** 
223      * 獲取手指在content界面滑動的速度。 
224      *  
225      * @return 滑動速度,以每秒鐘移動了多少像素值爲單位。 
226      */  
227     private int getScrollVelocity() {  
228         mVelocityTracker.computeCurrentVelocity(1000);  
229         int velocity = (int) mVelocityTracker.getXVelocity();  
230         return Math.abs(velocity);  
231     }  
232   
233     /** 
234      * 回收VelocityTracker對象。 
235      */  
236     private void recycleVelocityTracker() {  
237         mVelocityTracker.recycle();  
238         mVelocityTracker = null;  
239     }  
240   
241     class ScrollTask extends AsyncTask<Integer, Integer, Integer> {  
242   
243         @Override  
244         protected Integer doInBackground(Integer... speed) {  
245             int leftMargin = menuParams.leftMargin;  
246             // 根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時,跳出循環。  
247             while (true) {  
248                 leftMargin = leftMargin + speed[0];  
249                 if (leftMargin > rightEdge) {  
250                     leftMargin = rightEdge;  
251                     break;  
252                 }  
253                 if (leftMargin < leftEdge) {  
254                     leftMargin = leftEdge;  
255                     break;  
256                 }  
257                 publishProgress(leftMargin);  
258                 // 爲了要有滾動效果產生,每次循環使線程睡眠20毫秒,這樣肉眼纔可以看到滾動動畫。  
259                 sleep(20);  
260             }  
261             if (speed[0] > 0) {  
262                 isMenuVisible = true;  
263             } else {  
264                 isMenuVisible = false;  
265             }  
266             return leftMargin;  
267         }  
268   
269         @Override  
270         protected void onProgressUpdate(Integer... leftMargin) {  
271             menuParams.leftMargin = leftMargin[0];  
272             menu.setLayoutParams(menuParams);  
273         }  
274   
275         @Override  
276         protected void onPostExecute(Integer leftMargin) {  
277             menuParams.leftMargin = leftMargin;  
278             menu.setLayoutParams(menuParams);  
279         }  
280     }  
281   
282     /** 
283      * 使當前線程睡眠指定的毫秒數。 
284      *  
285      * @param millis 
286      *            指定當前線程睡眠多久,以毫秒爲單位 
287      */  
288     private void sleep(long millis) {  
289         try {  
290             Thread.sleep(millis);  
291         } catch (InterruptedException e) {  
292             e.printStackTrace();  
293         }  
294     }  
295 }
296     

對以上代碼解釋一下,首先初始化的時候調用initValues方法,在這裏面將內容佈局的寬度設定爲屏幕的寬度,菜單佈局的寬度設定爲屏幕的寬度減去menuPadding值,這樣能夠保證在菜單佈局展現的時候,仍有一部份內容佈局能夠看到。若是不在初始化的時候重定義兩個佈局寬度,就會按照layout文件裏面聲明的同樣,兩個佈局都是fill_parent,這樣就沒法實現滑動菜單的效果了。而後將菜單佈局的左偏移量設置爲負的菜單佈局的寬度,這樣菜單佈局就會被徹底隱藏,只有內容佈局會顯示在界面上。

以後給內容佈局註冊監聽事件,這樣當手指在內容佈局上滑動的時候就會觸發onTouch事件。在onTouch事件裏面,根據手指滑動的距離會改變菜單佈局的左偏移量,從而控制菜單佈局的顯示和隱藏。當手指離開屏幕的時候,會判斷應該滑動到菜單佈局仍是內容佈局,判斷依據是根據手指滑動的距離或者滑動的速度,細節能夠看代碼中的註釋。

 

最後是AndroidManifest.xml的代碼

 1 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 2     package="com.example.xqx_lianxi"
 3     android:versionCode="1"
 4     android:versionName="1.0" >
 5 
 6     <uses-sdk
 7         android:minSdkVersion="8"
 8         android:targetSdkVersion="18" />
 9 
10     <application
11         android:allowBackup="true"
12         android:icon="@drawable/ic_launcher"
13         android:label="@string/app_name"
14         android:theme="@style/AppTheme" >
15         <activity android:name="xqx.Main">
16             <intent-filter >
17                 <action android:name="android.intent.action.MAIN"/>
18                 <category android:name="android.intent.category.LAUNCHER"/>
19             </intent-filter>
20         </activity>
21     </application>
22 
23 </manifest>

效果圖:

未滑動時的圖

 

  滑動結束的圖

 

滑動過程當中的圖

相關文章
相關標籤/搜索