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

轉:http://blog.csdn.net/xiaanming/article/details/17374599html

 

很榮幸我可以成爲CSDN 2013年度博客之星評選的候選人,但願繼續獲得你們的支持與鼓勵,看到的朋友幫我投上寶貴的一票吧!java

 

       投票地址http://vote.blog.csdn.net/blogstaritem/blogstar2013/xiaanmingandroid

 

轉帖請註明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming),請尊重他人的辛勤勞動成果,謝謝!app

隨着移動互聯網的快速發展,它已經和咱們的生活息息相關了,在公交地鐵裏面都能看到不少人的人低頭看着本身的手機屏幕,今後「低頭族」一詞就產生了,做爲一名移動行業的開發人員,我本身也是一名「低頭族」,上下班時間在公交地鐵上看看新聞來打發下時間,有時候也會看看那些受歡迎的App的一些界面效果,爲何人家的app那麼受歡迎?跟用戶體驗跟UI設計也有直接的關係,最近在美團和大衆點評的App看到以下效果,我感受用戶好,很人性化,因此本身也嘗試着實現了下,接下來就講解下實現思路!ide

如上圖(2)咱們看到了,噹噹即搶購佈局向上滑動到導航欄佈局的時候,當即搶購佈局就貼在導航欄佈局下面,下面的其餘的佈局仍是能夠滑動,當咱們向下滑動的時候,當即搶購的佈局又隨着往下滑動了,看似有點複雜,可是一說思路可能你就頓時恍然大悟了。佈局

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

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

 

[html]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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裏面,界面仍是很簡單的.net

[html]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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類中的代碼設計

 

[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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.  * 博客地址:http://blog.csdn.net/xiaanming 
  10.  *  
  11.  * @author xiaanming 
  12.  * 
  13.  */  
  14. public class MyScrollView extends ScrollView {  
  15.     private OnScrollListener onScrollListener;  
  16.     /** 
  17.      * 主要是用在用戶手指離開MyScrollView,MyScrollView還在繼續滑動,咱們用來保存Y的距離,而後作比較 
  18.      */  
  19.     private int lastScrollY;  
  20.       
  21.     public MyScrollView(Context context) {  
  22.         this(context, null);  
  23.     }  
  24.       
  25.     public MyScrollView(Context context, AttributeSet attrs) {  
  26.         this(context, attrs, 0);  
  27.     }  
  28.   
  29.     public MyScrollView(Context context, AttributeSet attrs, int defStyle) {  
  30.         super(context, attrs, defStyle);  
  31.     }  
  32.       
  33.     /** 
  34.      * 設置滾動接口 
  35.      * @param onScrollListener 
  36.      */  
  37.     public void setOnScrollListener(OnScrollListener onScrollListener) {  
  38.         this.onScrollListener = onScrollListener;  
  39.     }  
  40.   
  41.   
  42.     /** 
  43.      * 用於用戶手指離開MyScrollView的時候獲取MyScrollView滾動的Y距離,而後回調給onScroll方法中 
  44.      */  
  45.     private Handler handler = new Handler() {  
  46.   
  47.         public void handleMessage(android.os.Message msg) {  
  48.             int scrollY = MyScrollView.this.getScrollY();  
  49.               
  50.             //此時的距離和記錄下的距離不相等,在隔5毫秒給handler發送消息  
  51.             if(lastScrollY != scrollY){  
  52.                 lastScrollY = scrollY;  
  53.                 handler.sendMessageDelayed(handler.obtainMessage(), 5);    
  54.             }  
  55.             if(onScrollListener != null){  
  56.                 onScrollListener.onScroll(scrollY);  
  57.             }  
  58.               
  59.         };  
  60.   
  61.     };   
  62.   
  63.     /** 
  64.      * 重寫onTouchEvent, 當用戶的手在MyScrollView上面的時候, 
  65.      * 直接將MyScrollView滑動的Y方向距離回調給onScroll方法中,當用戶擡起手的時候, 
  66.      * MyScrollView可能還在滑動,因此當用戶擡起手咱們隔5毫秒給handler發送消息,在handler處理 
  67.      * MyScrollView滑動的距離 
  68.      */  
  69.     @Override  
  70.     public boolean onTouchEvent(MotionEvent ev) {  
  71.         if(onScrollListener != null){  
  72.             onScrollListener.onScroll(lastScrollY = this.getScrollY());  
  73.         }  
  74.         switch(ev.getAction()){  
  75.         case MotionEvent.ACTION_UP:  
  76.              handler.sendMessageDelayed(handler.obtainMessage(), 5);    
  77.             break;  
  78.         }  
  79.         return super.onTouchEvent(ev);  
  80.     }  
  81.   
  82.   
  83.     /** 
  84.      *  
  85.      * 滾動的回調接口 
  86.      *  
  87.      * @author xiaanming 
  88.      * 
  89.      */  
  90.     public interface OnScrollListener{  
  91.         /** 
  92.          * 回調方法, 返回MyScrollView滑動的Y方向距離 
  93.          * @param scrollY 
  94.          *              、 
  95.          */  
  96.         public void onScroll(int scrollY);  
  97.     }  
  98.       
  99.       
  100.   
  101. }  

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

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

 

[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
 
  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.   
  133. }  


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

 


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

 

好了,今天的講解到此結束,有疑問的朋友請在下面留言

項目源碼,點擊下載

PS:你們有興趣的話能夠看看Android 仿美團網,大衆點評購買框懸浮效果之修改版

相關文章
相關標籤/搜索