Android實(shí)現(xiàn)美團(tuán)、大眾點(diǎn)評(píng)的購(gòu)買懸浮效果(ScrollView滾動(dòng)監(jiān)聽(tīng))
隨著移動(dòng)互聯(lián)網(wǎng)的快速發(fā)展,它已經(jīng)和我們的生活息息相關(guān)了,在公交地鐵里面都能看到很多人的人低頭看著自己的手機(jī)屏幕,從此“低頭族”一詞就產(chǎn)生了,作為一名移動(dòng)行業(yè)的開(kāi)發(fā)人員,我自己也是一名“低頭族”,上下班時(shí)間在公交地鐵上看看新聞來(lái)打發(fā)下時(shí)間,有時(shí)候也會(huì)看看那些受歡迎的App的一些界面效果,為什么人家的app那么受歡迎?跟用戶體驗(yàn)跟UI設(shè)計(jì)也有直接的關(guān)系,最近在美團(tuán)和大眾點(diǎn)評(píng)的App看到如下效果,我感覺(jué)用戶好,很人性化,所以自己也嘗試著實(shí)現(xiàn)了下,接下來(lái)就講解下實(shí)現(xiàn)思路!

如上圖(2)我們看到了,當(dāng)立即搶購(gòu)布局向上滑動(dòng)到導(dǎo)航欄布局的時(shí)候,立即搶購(gòu)布局就貼在導(dǎo)航欄布局下面,下面的其他的布局還是可以滑動(dòng),當(dāng)我們向下滑動(dòng)的時(shí)候,立即搶購(gòu)的布局又隨著往下滑動(dòng)了,看似有點(diǎn)復(fù)雜,但是一說(shuō)思路可能你就頓時(shí)恍然大悟了。
當(dāng)我們向上滑動(dòng)過(guò)程中,我們判斷立即搶購(gòu)的布局是否滑到導(dǎo)航欄布局下面,如果立即搶購(gòu)的上面頂?shù)搅藢?dǎo)航欄,我們新建一個(gè)立即搶購(gòu)的懸浮框來(lái)顯示在導(dǎo)航欄下面,這樣子就實(shí)現(xiàn)了立即搶購(gòu)貼在導(dǎo)航欄下面的效果啦,而當(dāng)我們向下滑動(dòng)的時(shí)候,當(dāng)立即搶購(gòu)布局的下面剛好到了剛剛新建的立即搶購(gòu)懸浮框的下面的時(shí)候,我們就移除立即搶購(gòu)懸浮框,可能說(shuō)的有點(diǎn)拗口,既然知道了思路,接下來(lái)我們就來(lái)實(shí)現(xiàn)效果。
新建一個(gè)Android項(xiàng)目,取名MeiTuanDemo,先看立即搶購(gòu)(buy_layout.xml)的布局,這里為了方便我直接從美團(tuán)上面截去了圖片
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/buy_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/buy" /> </LinearLayout>
立即搶購(gòu)的布局實(shí)現(xiàn)了,接下來(lái)實(shí)現(xiàn)主界面的布局,上面是導(dǎo)航欄布局,為了方便還是直接從美團(tuán)截取的圖片,然后下面的ViewPager布局,立即搶購(gòu)布局,其他布局 放在ScrollView里面,界面還是很簡(jiǎn)單的
<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:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="45dip"
android:src="@drawable/navigation_bar" />
<com.example.meituandemo.MyScrollView
android:id="@+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="@+id/iamge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/pic"
android:scaleType="centerCrop" />
<include
android:id="@+id/buy"
layout="@layout/buy_layout" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/one"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/one"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/one"
android:scaleType="centerCrop" />
</LinearLayout>
</com.example.meituandemo.MyScrollView>
</LinearLayout>
你會(huì)發(fā)現(xiàn)上面的主界面布局中并不是ScrollView,而是自定義的一個(gè)MyScrollView,接下來(lái)就看看MyScrollView類中的代碼
package com.example.meituandemo;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;
/**
* 博客地址:http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*/
public class MyScrollView extends ScrollView {
private OnScrollListener onScrollListener;
/**
* 主要是用在用戶手指離開(kāi)MyScrollView,MyScrollView還在繼續(xù)滑動(dòng),我們用來(lái)保存Y的距離,然后做比較
*/
private int lastScrollY;
public MyScrollView(Context context) {
this(context, null);
}
public MyScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 設(shè)置滾動(dòng)接口
* @param onScrollListener
*/
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
/**
* 用于用戶手指離開(kāi)MyScrollView的時(shí)候獲取MyScrollView滾動(dòng)的Y距離,然后回調(diào)給onScroll方法中
*/
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
int scrollY = MyScrollView.this.getScrollY();
//此時(shí)的距離和記錄下的距離不相等,在隔5毫秒給handler發(fā)送消息
if(lastScrollY != scrollY){
lastScrollY = scrollY;
handler.sendMessageDelayed(handler.obtainMessage(), 5);
}
if(onScrollListener != null){
onScrollListener.onScroll(scrollY);
}
};
};
/**
* 重寫(xiě)onTouchEvent, 當(dāng)用戶的手在MyScrollView上面的時(shí)候,
* 直接將MyScrollView滑動(dòng)的Y方向距離回調(diào)給onScroll方法中,當(dāng)用戶抬起手的時(shí)候,
* MyScrollView可能還在滑動(dòng),所以當(dāng)用戶抬起手我們隔5毫秒給handler發(fā)送消息,在handler處理
* MyScrollView滑動(dòng)的距離
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(onScrollListener != null){
onScrollListener.onScroll(lastScrollY = this.getScrollY());
}
switch(ev.getAction()){
case MotionEvent.ACTION_UP:
handler.sendMessageDelayed(handler.obtainMessage(), 5);
break;
}
return super.onTouchEvent(ev);
}
/**
*
* 滾動(dòng)的回調(diào)接口
*
* @author xiaanming
*
*/
public interface OnScrollListener{
/**
* 回調(diào)方法, 返回MyScrollView滑動(dòng)的Y方向距離
* @param scrollY
* 、
*/
public void onScroll(int scrollY);
}
}
一看代碼你也許明白了,就是對(duì)ScrollView的滾動(dòng)Y值進(jìn)行監(jiān)聽(tīng),我們知道ScrollView并沒(méi)有實(shí)現(xiàn)滾動(dòng)監(jiān)聽(tīng),所以我們必須自行實(shí)現(xiàn)對(duì)ScrollView的監(jiān)聽(tīng),我們很自然的想到在onTouchEvent()方法中實(shí)現(xiàn)對(duì)滾動(dòng)Y軸進(jìn)行監(jiān)聽(tīng),可是你會(huì)發(fā)現(xiàn),我們?cè)诨瑒?dòng)ScrollView的時(shí)候,當(dāng)我們手指離開(kāi)ScrollView。它可能還會(huì)繼續(xù)滑動(dòng)一段距離,所以我們選擇在用戶手指離開(kāi)的時(shí)候每隔5毫秒來(lái)判斷ScrollView是否停止滑動(dòng),并將ScrollView的滾動(dòng)Y值回調(diào)給OnScrollListener接口的onScroll(int scrollY)方法中,我們只需要對(duì)ScrollView調(diào)用我們只需要對(duì)ScrollView調(diào)用setOnScrollListener方法就能監(jiān)聽(tīng)到滾動(dòng)的Y值。
實(shí)現(xiàn)了對(duì)ScrollView滾動(dòng)的Y值進(jìn)行監(jiān)聽(tīng),接下來(lái)就簡(jiǎn)單了,我們只需要顯示立即搶購(gòu)懸浮框和移除懸浮框了,接下來(lái)看看主界面Activity的代碼編寫(xiě)
package com.example.meituandemo;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.LinearLayout;
import com.example.meituandemo.MyScrollView.OnScrollListener;
/**
* 博客地址:http://blog.csdn.net/xiaanming
*
* @author xiaanming
*
*/
public class MainActivity extends Activity implements OnScrollListener{
private MyScrollView myScrollView;
private LinearLayout mBuyLayout;
private WindowManager mWindowManager;
/**
* 手機(jī)屏幕寬度
*/
private int screenWidth;
/**
* 懸浮框View
*/
private static View suspendView;
/**
* 懸浮框的參數(shù)
*/
private static WindowManager.LayoutParams suspendLayoutParams;
/**
* 購(gòu)買布局的高度
*/
private int buyLayoutHeight;
/**
* myScrollView與其父類布局的頂部距離
*/
private int myScrollViewTop;
/**
* 購(gòu)買布局與其父類布局的頂部距離
*/
private int buyLayoutTop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myScrollView = (MyScrollView) findViewById(R.id.scrollView);
mBuyLayout = (LinearLayout) findViewById(R.id.buy);
myScrollView.setOnScrollListener(this);
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
screenWidth = mWindowManager.getDefaultDisplay().getWidth();
}
/**
* 窗口有焦點(diǎn)的時(shí)候,即所有的布局繪制完畢的時(shí)候,我們來(lái)獲取購(gòu)買布局的高度和myScrollView距離父類布局的頂部位置
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(hasFocus){
buyLayoutHeight = mBuyLayout.getHeight();
buyLayoutTop = mBuyLayout.getTop();
myScrollViewTop = myScrollView.getTop();
}
}
/**
* 滾動(dòng)的回調(diào)方法,當(dāng)滾動(dòng)的Y距離大于或者等于 購(gòu)買布局距離父類布局頂部的位置,就顯示購(gòu)買的懸浮框
* 當(dāng)滾動(dòng)的Y的距離小于 購(gòu)買布局距離父類布局頂部的位置加上購(gòu)買布局的高度就移除購(gòu)買的懸浮框
*
*/
@Override
public void onScroll(int scrollY) {
if(scrollY >= buyLayoutTop){
if(suspendView == null){
showSuspend();
}
}else if(scrollY <= buyLayoutTop + buyLayoutHeight){
if(suspendView != null){
removeSuspend();
}
}
}
/**
* 顯示購(gòu)買的懸浮框
*/
private void showSuspend(){
if(suspendView == null){
suspendView = LayoutInflater.from(this).inflate(R.layout.buy_layout, null);
if(suspendLayoutParams == null){
suspendLayoutParams = new LayoutParams();
suspendLayoutParams.type = LayoutParams.TYPE_PHONE; //懸浮窗的類型,一般設(shè)為2002,表示在所有應(yīng)用程序之上,但在狀態(tài)欄之下
suspendLayoutParams.format = PixelFormat.RGBA_8888;
suspendLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE; //懸浮窗的行為,比如說(shuō)不可聚焦,非模態(tài)對(duì)話框等等
suspendLayoutParams.gravity = Gravity.TOP; //懸浮窗的對(duì)齊方式
suspendLayoutParams.width = screenWidth;
suspendLayoutParams.height = buyLayoutHeight;
suspendLayoutParams.x = 0; //懸浮窗X的位置
suspendLayoutParams.y = myScrollViewTop; ////懸浮窗Y的位置
}
}
mWindowManager.addView(suspendView, suspendLayoutParams);
}
/**
* 移除購(gòu)買的懸浮框
*/
private void removeSuspend(){
if(suspendView != null){
mWindowManager.removeView(suspendView);
suspendView = null;
}
}
}
上面的代碼比較簡(jiǎn)單,根據(jù)ScrollView滑動(dòng)的距離來(lái)判斷顯示和移除懸浮框,懸浮框的實(shí)現(xiàn)主要是通過(guò)WindowManager這個(gè)類來(lái)實(shí)現(xiàn)的,調(diào)用這個(gè)類的addView方法用于添加一個(gè)懸浮框,removeView用于移除懸浮框。
通過(guò)上述代碼就實(shí)現(xiàn)了美團(tuán),大眾點(diǎn)評(píng)的這種效果,在運(yùn)行項(xiàng)目之前我們必須在AndroidManifest.xml中加入<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
我們運(yùn)行下項(xiàng)目看下效果吧

好了,今天的講解到此結(jié)束,有疑問(wèn)的朋友可以留言。
項(xiàng)目源碼,點(diǎn)擊下載
PS:大家有興趣的話可以看看Android仿美團(tuán)網(wǎng),大眾點(diǎn)評(píng)購(gòu)買框懸浮效果之修改版。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 實(shí)現(xiàn)控件懸浮效果實(shí)例代碼
- Android滑動(dòng)組件懸浮固定在頂部效果
- 詳解android使用ItemDecoration 懸浮導(dǎo)航欄效果
- Android 沉浸式狀態(tài)欄及懸浮效果
- Android Listview多tab上滑懸浮效果
- Android實(shí)現(xiàn)帶磁性的懸浮窗體效果
- Android實(shí)現(xiàn)桌面懸浮窗、蒙板效果實(shí)例代碼
- 模仿美團(tuán)點(diǎn)評(píng)的Android應(yīng)用中價(jià)格和購(gòu)買欄懸浮固定的效果
- Android利用懸浮按鈕實(shí)現(xiàn)翻頁(yè)效果
- Android仿美團(tuán)網(wǎng)、大眾點(diǎn)評(píng)購(gòu)買框懸浮效果修改版
相關(guān)文章
Android Studio編寫(xiě)AIDL文件后如何實(shí)現(xiàn)自動(dòng)編譯生成
這篇文章主要介紹了Android Studio編寫(xiě)AIDL文件后如何實(shí)現(xiàn)自動(dòng)編譯生成,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android開(kāi)發(fā)之全屏與非全屏的切換設(shè)置方法小結(jié)
這篇文章主要介紹了Android開(kāi)發(fā)之全屏與非全屏的切換設(shè)置方法,結(jié)合實(shí)例形式分析了Android全屏切換靜態(tài)與動(dòng)態(tài)兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2017-08-08
Android設(shè)置鬧鐘相對(duì)完善的解決方案
這篇文章主要為大家詳細(xì)介紹了Android設(shè)置鬧鐘相對(duì)完善的解決方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
RecyclerView實(shí)現(xiàn)縱向和橫向滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)縱向和橫向滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android自定義View實(shí)現(xiàn)垂直時(shí)間軸布局
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)垂直時(shí)間軸布局的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android中fragment與activity之間的交互(兩種實(shí)現(xiàn)方式)
本篇文章主要介紹了Android中fragment與activity之間的交互(兩種實(shí)現(xiàn)方式),相信對(duì)大家學(xué)習(xí)會(huì)有很好的幫助,需要的朋友一起來(lái)看下吧2016-12-12
淺談AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)的分析說(shuō)明
本篇文章是對(duì)AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
在當(dāng)前Activity之上創(chuàng)建懸浮view之WindowManager懸浮窗效果
這篇文章主要介紹了在當(dāng)前Activity之上創(chuàng)建懸浮view之WindowManager懸浮窗效果的相關(guān)資料,需要的朋友可以參考下2016-01-01

