Android仿餓了么地圖滑動(dòng)懸停華麗效果
一、滑動(dòng)懸停效果
最近仿餓了么地圖頁面做了個(gè)滑動(dòng)懸停效果,分享一下。
二、功能效果圖




三、實(shí)現(xiàn)滑動(dòng)懸停華麗效果
1.功能實(shí)現(xiàn)主要控件
主要使用CoordinatorLayout + AppBarLayout + NestedScrollView 組合使用實(shí)現(xiàn)地圖背景,滑動(dòng)懸停華麗效果。(注:demo地圖使用的是高德地圖)
2.先導(dǎo)包
在build.gradle文件中,需要哪些功能,就導(dǎo)哪幾個(gè)包,不需要的功能可以不導(dǎo)入,只導(dǎo)入高德地圖的包就行了。
dependencies {/*高德地圖*/implementation 'com.amap.api:3dmap:latest.integration'/*注解*/api 'com.jakewharton:butterknife:10.2.0'annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'//狀態(tài)欄、titlebarimplementation 'com.gyf.immersionbar:immersionbar:3.0.0-beta05'implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0-beta05'//cardViewimplementation 'androidx.cardview:cardview:1.0.0'}
3.清單文件AndroidManifest.xml增加權(quán)限
如果你的高德地圖不顯示,都是灰色方格;可以將下面的代碼放到清單文件AndroidManifest.xml中
<!--允許程序打開網(wǎng)絡(luò)套接字--><uses-permission android:name="android.permission.INTERNET" /><!--允許程序設(shè)置內(nèi)置sd卡的寫權(quán)限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--允許程序獲取網(wǎng)絡(luò)狀態(tài)--><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!--允許程序訪問WiFi網(wǎng)絡(luò)信息--><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><!--允許程序讀寫手機(jī)狀態(tài)和身份--><uses-permission android:name="android.permission.READ_PHONE_STATE" /><!--允許程序訪問CellID或WiFi熱點(diǎn)來獲取粗略的位置--><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
4. 布局文件 activity_effects.xml
(注:每個(gè)人的需求不同,布局僅供參考,大家可以根據(jù)實(shí)際情況,寫的簡(jiǎn)單一些,有部分代碼,并不需要。布局中涉及的圖片和部分色值,大家自己隨意放一個(gè)就行。)
<?xml version="1.0" encoding="utf-8"?><androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/coordinator"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/appbg"tools:context="com.sun.coordinatordemo.see.EffectsActivity"><!--地圖全屏顯示 layout_height="match_parent" 非全屏就設(shè)置地圖的高度--><com.amap.api.maps.MapViewandroid:id="@+id/map"android:layout_width="match_parent"android:layout_height="470dp"/><com.google.android.material.appbar.AppBarLayoutandroid:id="@+id/appBar"android:layout_width="match_parent"android:layout_height="400dp"android:background="@color/blue_two"android:paddingTop="27dp"app:elevation="0dp"app:layout_behavior="@string/behavior"><com.google.android.material.appbar.CollapsingToolbarLayoutandroid:id="@+id/collapsingLayout"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"app:collapsedTitleGravity="center"app:layout_scrollFlags="scroll|exitUntilCollapsed"><!--根據(jù)實(shí)際需求設(shè)置:設(shè)置title最后居中顯示,Toolbar需要再單獨(dú)設(shè)置。若沒有居中要求,直接在此設(shè)置返回按鈕即可 app:navigationIcon="@drawable/back_black",--><androidx.appcompat.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_collapseMode="pin"app:layout_scrollFlags="scroll" /></com.google.android.material.appbar.CollapsingToolbarLayout></com.google.android.material.appbar.AppBarLayout><!--設(shè)置返回按鈕的動(dòng)態(tài)變化。此Toolbar需要單獨(dú)設(shè)置--><androidx.appcompat.widget.Toolbarandroid:id="@+id/bar_nav_return"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:layout_marginTop="27dp"app:navigationIcon="@drawable/back_black" /><androidx.core.widget.NestedScrollViewandroid:id="@+id/nestScrollView"android:layout_width="match_parent"android:layout_height="match_parent"android:fillViewport="true"android:scrollbars="none"app:layout_behavior="@string/appbar_scrolling_view_behavior"><!-- android:background="@color/appbg"--><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!--隨著動(dòng)畫 顏色背景漸變基礎(chǔ)設(shè)置--><Viewandroid:id="@+id/v_showBG"android:layout_width="match_parent"android:layout_height="200dp"android:background="@drawable/bg_blue_gradual_change" /><!--想看到背景地圖就將這里的view背景去掉--><Viewandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:background="@color/appbg" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:id="@+id/ll_title_isShow"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_marginTop="5dp"android:layout_marginRight="15dp"android:background="@color/white"android:orientation="horizontal"android:padding="15dp"><ImageViewandroid:id="@+id/iv_tx"android:layout_width="45dp"android:layout_height="45dp"android:background="@mipmap/ic_launcher_round" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:orientation="vertical"><TextViewandroid:id="@+id/tv_order_status"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="正在取貨中"android:textColor="#222222"android:textSize="18sp"android:textStyle="bold" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="距離商家"android:textColor="@color/purple_700"android:textSize="13sp" /><TextViewandroid:id="@+id/tv_order_distance"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="約198km"android:textColor="@color/purple_700"android:textSize="13sp" /></LinearLayout></LinearLayout></LinearLayout><androidx.cardview.widget.CardViewandroid:id="@+id/cardview"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/ll_title_isShow"android:layout_marginLeft="15dp"android:layout_marginTop="10dp"android:layout_marginRight="15dp"app:cardBackgroundColor="@color/white"app:cardCornerRadius="5dp"app:cardElevation="0dp"app:contentPadding="15dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="基本信息"android:textColor="#222222"android:textSize="15sp"android:textStyle="bold" /><TextViewandroid:layout_width="wrap_content"android:layout_height="60dp"android:layout_marginLeft="10dp"android:text="測(cè)試文案 測(cè)試文案"android:textColor="#222222"android:textSize="13sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="60dp"android:layout_marginLeft="10dp"android:text="測(cè)試文案 測(cè)試文案"android:textColor="#222222"android:textSize="13sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="60dp"android:layout_marginLeft="10dp"android:text="測(cè)試文案 測(cè)試文案"android:textColor="#222222"android:textSize="13sp" /></LinearLayout></androidx.cardview.widget.CardView></LinearLayout></FrameLayout></LinearLayout></androidx.core.widget.NestedScrollView></androidx.coordinatorlayout.widget.CoordinatorLayout>
5.String.xml文件中設(shè)置
<!-- 找到自定義的AppBarBehavior文件,復(fù)制引用,這里是 AppBarBehavior的路徑 --><string name="behavior">com.sun.coordinatordemo.see.AppBarBehavior</string>
6.漸變?cè)O(shè)置
bg_blue_gradual_change.xml文件放到drawable中即可,
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><gradientandroid:angle="90"android:type="linear"android:startColor="#FFEDEFF3"android:endColor="#163de7"/><!-- #FF0013B7--><!-- android:centerColor="#FF4654C8"--></shape>
7.自定義view: AppBarBehavior
(注:自定義 AppBarBehavior.class,此文件放在你的工具類view中即可)
package com.sun.coordinatordemo.see;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import androidx.annotation.NonNull;import androidx.coordinatorlayout.widget.CoordinatorLayout;import androidx.core.view.ViewCompat;import com.google.android.material.appbar.AppBarLayout;/*** 仿餓了么地圖滑動(dòng)特效*/public class AppBarBehavior extends AppBarLayout.Behavior {/*** nestedScrollview的滑動(dòng)距離*/int ay = 0;/*** nestedScrollview 沒有滑動(dòng)到頂部之前的可滑動(dòng)最大距離*/private int minY = 0;public AppBarBehavior() {}public AppBarBehavior(Context context, AttributeSet attrs) {super(context, attrs);}/*** AppBarLayout布局時(shí)調(diào)用** @param parent 父布局CoordinatorLayout* @param abl 使用此Behavior的AppBarLayout* @param layoutDirection 布局方向* @return 返回true表示子View重新布局,返回false表示請(qǐng)求默認(rèn)布局*/public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {return super.onLayoutChild(parent, abl, layoutDirection);}/*** 當(dāng)CoordinatorLayout的子View嘗試發(fā)起嵌套滾動(dòng)時(shí)調(diào)用** @param parent 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param directTargetChild CoordinatorLayout的子View,或者是包含嵌套滾動(dòng)操作的目標(biāo)View* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)* @param nestedScrollAxes 嵌套滾動(dòng)的方向* @return 返回true表示接受滾動(dòng)*/public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;}/*** 當(dāng)嵌套滾動(dòng)已由CoordinatorLayout接受時(shí)調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param directTargetChild CoordinatorLayout的子View,或者是包含嵌套滾動(dòng)操作的目標(biāo)View* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)*/public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type);}/*** 當(dāng)準(zhǔn)備開始嵌套滾動(dòng)時(shí)調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)* @param dx 用戶在水平方向上滑動(dòng)的像素?cái)?shù)* @param dy 用戶在垂直方向上滑動(dòng)的像素?cái)?shù)* @param consumed 輸出參數(shù),consumed[0]為水平方向應(yīng)該消耗的距離,consumed[1]為垂直方向應(yīng)該消耗的距離*/public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {if (minY == 0) {minY = target.getHeight() + child.getHeight() - coordinatorLayout.getHeight();}/*** 如果nestedScrollview 布局內(nèi)容少?zèng)]有內(nèi)部滑動(dòng),這時(shí)需要我們進(jìn)行處理* 當(dāng)nestedScrollview 內(nèi)容很多時(shí),我們不需要任何事情* */if (target.getScrollY() <= 0) {ay += dy;if (dy > 0 && ay >= minY) {dy = minY + dy - ay;//這里之所以沒有讓dy = 0;因?yàn)楫?dāng)快速滑動(dòng)時(shí)就會(huì)有問題,快速滑動(dòng)時(shí)dy的變化很大,自行琢磨一下,還不懂加群交流ay = minY;}if (dy < 0 && ay <= 0) {ay = 0;dy = 0;}}super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);}/*** 嵌套滾動(dòng)時(shí)調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)* @param dxConsumed 由目標(biāo)View滾動(dòng)操作消耗的水平像素?cái)?shù)* @param dyConsumed 由目標(biāo)View滾動(dòng)操作消耗的垂直像素?cái)?shù)* @param dxUnconsumed 由用戶請(qǐng)求但是目標(biāo)View滾動(dòng)操作未消耗的水平像素?cái)?shù)* @param dyUnconsumed 由用戶請(qǐng)求但是目標(biāo)View滾動(dòng)操作未消耗的垂直像素?cái)?shù)*/public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);}/*** 當(dāng)嵌套滾動(dòng)的子View準(zhǔn)備快速滾動(dòng)時(shí)調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)* @param velocityX 水平方向的速度* @param velocityY 垂直方向的速度* @return 如果Behavior消耗了快速滾動(dòng)返回true*/public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);}/*** 當(dāng)嵌套滾動(dòng)的子View快速滾動(dòng)時(shí)調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)* @param velocityX 水平方向的速度* @param velocityY 垂直方向的速度* @param consumed 如果嵌套的子View消耗了快速滾動(dòng)則為true* @return 如果Behavior消耗了快速滾動(dòng)返回true*/public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);}/*** 當(dāng)觸摸時(shí)調(diào)用** @param parent 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param ev 手勢(shì)事件*/public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {return super.onTouchEvent(parent, child, ev);}/*** 當(dāng)觸摸想要攔截時(shí)調(diào)用 關(guān)鍵所在 true 不攔截 false 攔截AppBarLayout的手勢(shì)觸摸** @param parent 父布局CoordinatorLayout* @param child 使用此Behavior的AppBarLayout* @param ev 手勢(shì)事件*/public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {return false;}/*** 嵌套滾動(dòng)結(jié)束時(shí)被調(diào)用** @param coordinatorLayout 父布局CoordinatorLayout* @param abl 使用此Behavior的AppBarLayout* @param target 發(fā)起嵌套滾動(dòng)的目標(biāo)View(即AppBarLayout下面的ScrollView或RecyclerView)*/public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {super.onStopNestedScroll(coordinatorLayout, abl, target, type);}}
8.滑動(dòng)效果具體實(shí)現(xiàn) EffectsActivity.class
import android.graphics.Typeface;import android.os.Bundle;import android.view.View;import android.widget.LinearLayout;import androidx.annotation.Nullable;import androidx.appcompat.app.AppCompatActivity;import androidx.appcompat.widget.Toolbar;import androidx.coordinatorlayout.widget.CoordinatorLayout;import androidx.core.widget.NestedScrollView;import com.amap.api.maps.AMap;import com.amap.api.maps.MapView;import com.google.android.material.appbar.AppBarLayout;import com.google.android.material.appbar.CollapsingToolbarLayout;import com.gyf.immersionbar.ImmersionBar;import com.sun.coordinatordemo.R;import butterknife.BindView;import butterknife.ButterKnife;import butterknife.OnClick;/*** @author Promise Sun*/public class EffectsActivity extends AppCompatActivity {(R.id.appBar)AppBarLayout appBarLayout;(R.id.toolbar)Toolbar toolbar;(R.id.bar_nav_return)Toolbar bar_nav_return;(R.id.nestScrollView)NestedScrollView scrollView;(R.id.ll_title_isShow)LinearLayout ll_title_isShow;(R.id.v_showBG)View v_showBG;(R.id.collapsingLayout)CollapsingToolbarLayout collapsingLayout;private CoordinatorLayout.LayoutParams layoutParams;private AMap aMap;private MapView mapView;({R.id.bar_nav_return})public void onViewClicked(View view){switch (view.getId()) {case R.id.bar_nav_return:finish();break;}}protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_effects);ButterKnife.bind(this);ImmersionBar.with(this).reset().statusBarDarkFont(true).navigationBarDarkIcon(true).init();bar_nav_return.setNavigationIcon(getResources().getDrawable(R.drawable.back_black));//使用高德地圖mapView = findViewById(R.id.map);mapView.onCreate(savedInstanceState);if (aMap == null) {aMap = mapView.getMap();// 隱藏地圖放大、縮小按鈕aMap.getUiSettings().setZoomControlsEnabled(false);//不顯示指南針aMap.getUiSettings().setCompassEnabled(true);//顯示比例尺// aMap.getUiSettings().setScaleControlsEnabled(true);}toolbar.setTitle("正在取貨中");//動(dòng)態(tài)title文案//title顏色設(shè)置collapsingLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white));collapsingLayout.setExpandedTitleColor(getResources().getColor(R.color.white));//title字體加粗設(shè)置collapsingLayout.setCollapsedTitleTypeface(Typeface.defaultFromStyle(Typeface.BOLD));collapsingLayout.setExpandedTitleTypeface(Typeface.defaultFromStyle(Typeface.BOLD));//設(shè)置整體的UI從左右有邊距,隨著滑動(dòng),變?yōu)闊o邊距。是否需要此效果,大家可以自己打開下面注釋代碼,試試效果。// 必須給 NestedScrollView在布局文件中設(shè)置一個(gè)背景// layoutParams = (CoordinatorLayout.LayoutParams) scrollView.getLayoutParams();// layoutParams.setMargins(30, 0, 30, 0);// scrollView.setLayoutParams(layoutParams);appBarLayout.addOnOffsetChangedListener((appBarLayout, scrollY) -> {// float a = (float) 30 / appBarLayout.getTotalScrollRange();// int side = (int) Math.rint(a * scrollY + 30);// layoutParams.setMargins(side, 0, side, 0);// scrollView.setLayoutParams(layoutParams);if (Math.abs(scrollY) > 0) {//背景漸變float alpha = (float) Math.abs(scrollY) / appBarLayout.getTotalScrollRange();appBarLayout.setAlpha(alpha);v_showBG.getBackground().mutate().setAlpha(Math.round(alpha * 255));// scrollView.getBackground().mutate().setAlpha(Math.round(alpha * 255));//狀態(tài)欄設(shè)置ImmersionBar.with(this).reset().statusBarDarkFont(false).navigationBarDarkIcon(false).init();// 返回按鈕bar_nav_return.setNavigationIcon(getResources().getDrawable(R.drawable.white_back));} else {appBarLayout.setAlpha(0);v_showBG.getBackground().mutate().setAlpha(0);// scrollView.getBackground().mutate().setAlpha(0);//狀態(tài)欄設(shè)置ImmersionBar.with(this).reset().statusBarDarkFont(true).navigationBarDarkIcon(true).init();// 返回按鈕bar_nav_return.setNavigationIcon(getResources().getDrawable(R.drawable.back_black));}});}protected void onPause() {super.onPause();mapView.onPause();}protected void onResume() {super.onResume();mapView.onResume();}protected void onDestroy() {super.onDestroy();mapView.onDestroy();}protected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);mapView.onSaveInstanceState(outState);}}
需要源碼的童鞋在【龍旋】公眾號(hào)對(duì)話框回復(fù)【地圖滑動(dòng)】即可獲取哦!
到這里就結(jié)束啦。
評(píng)論
圖片
表情
