Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android監聽ScrollView滾動實現仿美團、大眾點評的購買懸浮效果

Android監聽ScrollView滾動實現仿美團、大眾點評的購買懸浮效果

編輯:Android開發實例

最近在美團和大眾點評的App看到如下效果,我感覺用戶好,很人性化,所以自己也嘗試著實現了下,接下來就講解下實現思路!
 

 

如上圖(2)我們看到了,當立即搶購布局向上滑動到導航欄布局的時候,立即搶購布局就貼在導航欄布局下面,下面的其他的布局還是可以滑動,當我們向下滑動的時候,立即搶購的布局又隨著往下滑動了,看似有點復雜,但是一說思路可能你就頓時恍然大悟了。

當我們向上滑動過程中,我們判斷立即搶購的布局是否滑到導航欄布局下面,如果立即搶購的上面頂到了導航欄,我們新建一個立即搶購的懸浮框來顯示在導航欄下面,這樣子就實現了立即搶購貼在導航欄下面的效果啦,而當我們向下滑動的時候,當立即搶購布局的下面剛好到了剛剛新建的立即搶購懸浮框的下面的時候,我們就移除立即搶購懸浮框,可能說的有點拗口,既然知道了思路,接下來我們就來實現效果。

新建一個Android項目,取名MeiTuanDemo,先看立即搶購(buy_layout.xml)的布局,這裡為了方便我直接從美團上面截去了圖片

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:orientation="horizontal" 
  4.     android:layout_width="fill_parent" 
  5.     android:layout_height="wrap_content" > 
  6.  
  7.     <ImageView 
  8.         android:id="@+id/buy_layout" 
  9.         android:layout_width="fill_parent" 
  10.         android:layout_height="wrap_content" 
  11.         android:background="@drawable/buy" /> 
  12.  
  13. </LinearLayout> 

 

立即搶購的布局實現了,接下來實現主界面的布局,上面是導航欄布局,為了方便還是直接從美團截取的圖片,然後下面的ViewPager布局,立即搶購布局,其他布局 放在ScrollView裡面,界面還是很簡單的

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     xmlns:tools="http://schemas.android.com/tools" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" 
  5.     android:orientation="vertical"  > 
  6.  
  7.       <ImageView 
  8.         android:id="@+id/imageView1" 
  9.         android:scaleType="centerCrop" 
  10.         android:layout_width="match_parent" 
  11.         android:layout_height="45dip" 
  12.         android:src="@drawable/navigation_bar" /> 
  13.           
  14.  
  15.     <com.example.meituandemo.MyScrollView 
  16.         android:id="@+id/scrollView" 
  17.         android:layout_width="fill_parent" 
  18.         android:layout_height="fill_parent" > 
  19.  
  20.         <LinearLayout 
  21.             android:layout_width="match_parent" 
  22.             android:layout_height="wrap_content" 
  23.             android:orientation="vertical" > 
  24.  
  25.             <ImageView 
  26.                 android:id="@+id/iamge" 
  27.                 android:layout_width="match_parent" 
  28.                 android:layout_height="wrap_content" 
  29.                 android:background="@drawable/pic" 
  30.                 android:scaleType="centerCrop" /> 
  31.  
  32.             <include 
  33.                 android:id="@+id/buy" 
  34.                 layout="@layout/buy_layout" /> 
  35.  
  36.             <ImageView 
  37.                 android:layout_width="match_parent" 
  38.                 android:layout_height="wrap_content" 
  39.                 android:background="@drawable/one" 
  40.                 android:scaleType="centerCrop" /> 
  41.  
  42.             <ImageView 
  43.                 android:layout_width="match_parent" 
  44.                 android:layout_height="wrap_content" 
  45.                 android:background="@drawable/one" 
  46.                 android:scaleType="centerCrop" /> 
  47.  
  48.             <ImageView 
  49.                 android:layout_width="match_parent" 
  50.                 android:layout_height="wrap_content" 
  51.                 android:background="@drawable/one" 
  52.                 android:scaleType="centerCrop" /> 
  53.         </LinearLayout> 
  54.     </com.example.meituandemo.MyScrollView> 
  55.  
  56. </LinearLayout> 

你會發現上面的主界面布局中並不是ScrollView,而是自定義的一個MyScrollView,接下來就看看MyScrollView類中的代碼

  1. package com.example.meituandemo;  
  2.  
  3. import android.content.Context;  
  4. import android.os.Handler;  
  5. import android.util.AttributeSet;  
  6. import android.view.MotionEvent;  
  7. import android.widget.ScrollView;  
  8. /**  
  9.  *   
  10.  * @author xiaanming  
  11.  *  
  12.  */ 
  13. public class MyScrollView extends ScrollView {  
  14.     private OnScrollListener onScrollListener;  
  15.     /**  
  16.      * 主要是用在用戶手指離開MyScrollView,MyScrollView還在繼續滑動,我們用來保存Y的距離,然後做比較  
  17.      */ 
  18.     private int lastScrollY;  
  19.       
  20.     public MyScrollView(Context context) {  
  21.         this(context, null);  
  22.     }  
  23.       
  24.     public MyScrollView(Context context, AttributeSet attrs) {  
  25.         this(context, attrs, 0);  
  26.     }  
  27.  
  28.     public MyScrollView(Context context, AttributeSet attrs, int defStyle) {  
  29.         super(context, attrs, defStyle);  
  30.     }  
  31.       
  32.     /**  
  33.      * 設置滾動接口  
  34.      * @param onScrollListener  
  35.      */ 
  36.     public void setOnScrollListener(OnScrollListener onScrollListener) {  
  37.         this.onScrollListener = onScrollListener;  
  38.     }  
  39.  
  40.  
  41.     /**  
  42.      * 用於用戶手指離開MyScrollView的時候獲取MyScrollView滾動的Y距離,然後回調給onScroll方法中  
  43.      */ 
  44.     private Handler handler = new Handler() {  
  45.  
  46.         public void handleMessage(android.os.Message msg) {  
  47.             int scrollY = MyScrollView.this.getScrollY();  
  48.               
  49.             //此時的距離和記錄下的距離不相等,在隔5毫秒給handler發送消息  
  50.             if(lastScrollY != scrollY){  
  51.                 lastScrollY = scrollY;  
  52.                 handler.sendMessageDelayed(handler.obtainMessage(), 5);    
  53.             }  
  54.             if(onScrollListener != null){  
  55.                 onScrollListener.onScroll(scrollY);  
  56.             }  
  57.               
  58.         };  
  59.  
  60.     };   
  61.  
  62.     /**  
  63.      * 重寫onTouchEvent, 當用戶的手在MyScrollView上面的時候,  
  64.      * 直接將MyScrollView滑動的Y方向距離回調給onScroll方法中,當用戶抬起手的時候,  
  65.      * MyScrollView可能還在滑動,所以當用戶抬起手我們隔5毫秒給handler發送消息,在handler處理  
  66.      * MyScrollView滑動的距離  
  67.      */ 
  68.     @Override 
  69.     public boolean onTouchEvent(MotionEvent ev) {  
  70.         if(onScrollListener != null){  
  71.             onScrollListener.onScroll(lastScrollY = this.getScrollY());  
  72.         }  
  73.         switch(ev.getAction()){  
  74.         case MotionEvent.ACTION_UP:  
  75.              handler.sendMessageDelayed(handler.obtainMessage(), 5);    
  76.             break;  
  77.         }  
  78.         return super.onTouchEvent(ev);  
  79.     }  
  80.  
  81.  
  82.     /**  
  83.      *   
  84.      * 滾動的回調接口  
  85.      *   
  86.      * @author xiaanming  
  87.      *  
  88.      */ 
  89.     public interface OnScrollListener{  
  90.         /**  
  91.          * 回調方法, 返回MyScrollView滑動的Y方向距離  
  92.          * @param scrollY  
  93.          *              、  
  94.          */ 
  95.         public void onScroll(int scrollY);  
  96.     }  
  97.       
  98.       
  99.  

一看代碼你也許明白了,就是對ScrollView的滾動Y值進行監聽,我們知道ScrollView並沒有實現滾動監聽,所以我們必須自行實現對ScrollView的監聽,我們很自然的想到在onTouchEvent()方法中實現對滾動Y軸進行監聽,可是你會發現,我們在滑動ScrollView的時候,當我們手指離開ScrollView。它可能還會繼續滑動一段距離,所以我們選擇在用戶手指離開的時候每隔5毫秒來判斷ScrollView是否停止滑動,並將ScrollView的滾動Y值回調給OnScrollListener接口的onScroll(int scrollY)方法中,我們只需要對ScrollView調用我們只需要對ScrollView調用setOnScrollListener方法就能監聽到滾動的Y值。
 

實現了對ScrollView滾動的Y值進行監聽,接下來就簡單了,我們只需要顯示立即搶購懸浮框和移除懸浮框了,接下來看看主界面Activity的代碼編寫

  1. package com.example.meituandemo;  
  2.  
  3. import android.app.Activity;  
  4. import android.content.Context;  
  5. import android.graphics.PixelFormat;  
  6. import android.os.Bundle;  
  7. import android.view.Gravity;  
  8. import android.view.LayoutInflater;  
  9. import android.view.View;  
  10. import android.view.WindowManager;  
  11. import android.view.WindowManager.LayoutParams;  
  12. import android.widget.LinearLayout;  
  13. import com.example.meituandemo.MyScrollView.OnScrollListener;  
  14. /**  
  15.  * 博客地址:http://blog.csdn.net/xiaanming  
  16.  *   
  17.  * @author xiaanming  
  18.  *  
  19.  */ 
  20. public class MainActivity extends Activity implements OnScrollListener{  
  21.     private MyScrollView myScrollView;  
  22.     private LinearLayout mBuyLayout;  
  23.     private WindowManager mWindowManager;  
  24.     /**  
  25.      * 手機屏幕寬度  
  26.      */ 
  27.     private int screenWidth;  
  28.     /**  
  29.      * 懸浮框View  
  30.      */ 
  31.     private static View suspendView;  
  32.     /**  
  33.      * 懸浮框的參數  
  34.      */ 
  35.     private static WindowManager.LayoutParams suspendLayoutParams;  
  36.     /**  
  37.      * 購買布局的高度  
  38.      */ 
  39.     private int buyLayoutHeight;  
  40.     /**  
  41.      * myScrollView與其父類布局的頂部距離  
  42.      */ 
  43.     private int myScrollViewTop;  
  44.  
  45.     /**  
  46.      * 購買布局與其父類布局的頂部距離  
  47.      */ 
  48.     private int buyLayoutTop;  
  49.       
  50.  
  51.     @Override 
  52.     protected void onCreate(Bundle savedInstanceState) {  
  53.         super.onCreate(savedInstanceState);  
  54.         setContentView(R.layout.activity_main);  
  55.           
  56.         myScrollView = (MyScrollView) findViewById(R.id.scrollView);  
  57.         mBuyLayout = (LinearLayout) findViewById(R.id.buy);  
  58.           
  59.         myScrollView.setOnScrollListener(this);  
  60.         mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);  
  61.         screenWidth = mWindowManager.getDefaultDisplay().getWidth();    
  62.     }  
  63.  
  64.     /**  
  65.      * 窗口有焦點的時候,即所有的布局繪制完畢的時候,我們來獲取購買布局的高度和myScrollView距離父類布局的頂部位置  
  66.      */ 
  67.     @Override    
  68.     public void onWindowFocusChanged(boolean hasFocus) {    
  69.         super.onWindowFocusChanged(hasFocus);    
  70.         if(hasFocus){  
  71.             buyLayoutHeight = mBuyLayout.getHeight();  
  72.             buyLayoutTop = mBuyLayout.getTop();  
  73.               
  74.             myScrollViewTop = myScrollView.getTop();  
  75.         }  
  76.     }   
  77.  
  78.  
  79.  
  80.     /**  
  81.      * 滾動的回調方法,當滾動的Y距離大於或者等於 購買布局距離父類布局頂部的位置,就顯示購買的懸浮框  
  82.      * 當滾動的Y的距離小於 購買布局距離父類布局頂部的位置加上購買布局的高度就移除購買的懸浮框  
  83.      *   
  84.      */ 
  85.     @Override 
  86.     public void onScroll(int scrollY) {  
  87.         if(scrollY >= buyLayoutTop){  
  88.             if(suspendView == null){  
  89.                 showSuspend();  
  90.             }  
  91.         }else if(scrollY <= buyLayoutTop + buyLayoutHeight){  
  92.             if(suspendView != null){  
  93.                 removeSuspend();  
  94.             }  
  95.         }  
  96.     }  
  97.  
  98.  
  99.     /**  
  100.      * 顯示購買的懸浮框  
  101.      */ 
  102.     private void showSuspend(){  
  103.         if(suspendView == null){  
  104.             suspendView = LayoutInflater.from(this).inflate(R.layout.buy_layout, null);  
  105.             if(suspendLayoutParams == null){  
  106.                 suspendLayoutParams = new LayoutParams();  
  107.                 suspendLayoutParams.type = LayoutParams.TYPE_PHONE; //懸浮窗的類型,一般設為2002,表示在所有應用程序之上,但在狀態欄之下   
  108.                 suspendLayoutParams.format = PixelFormat.RGBA_8888;   
  109.                 suspendLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL    
  110.                          | LayoutParams.FLAG_NOT_FOCUSABLE;  //懸浮窗的行為,比如說不可聚焦,非模態對話框等等   
  111.                 suspendLayoutParams.gravity = Gravity.TOP;  //懸浮窗的對齊方式  
  112.                 suspendLayoutParams.width = screenWidth;  
  113.                 suspendLayoutParams.height = buyLayoutHeight;    
  114.                 suspendLayoutParams.x = 0;  //懸浮窗X的位置  
  115.                 suspendLayoutParams.y = myScrollViewTop;  ////懸浮窗Y的位置  
  116.             }  
  117.         }  
  118.           
  119.         mWindowManager.addView(suspendView, suspendLayoutParams);  
  120.     }  
  121.       
  122.       
  123.     /**  
  124.      * 移除購買的懸浮框  
  125.      */ 
  126.     private void removeSuspend(){  
  127.         if(suspendView != null){  
  128.             mWindowManager.removeView(suspendView);  
  129.             suspendView = null;  
  130.         }  
  131.     }  
  132.  

上面的代碼比較簡單,根據ScrollView滑動的距離來判斷顯示和移除懸浮框,懸浮框的實現主要是通過WindowManager這個類來實現的,調用這個類的addView方法用於添加一個懸浮框,removeView用於移除懸浮框。
通過上述代碼就實現了美團,大眾點評的這種效果,在運行項目之前我們必須在AndroidManifest.xml中加入<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />


我們運行下項目看下效果吧


 

 

源碼下載

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved