效果图:

Android 仿美团悬浮购物车显示隐藏_购物车显示隐藏

这是美团的效果

Android 仿美团悬浮购物车显示隐藏_仿美团悬浮购物车_02Android 仿美团悬浮购物车显示隐藏_购物车显示隐藏_03

通过效果图可以看到 静止的时候是购物车图标是显示的,滑动的时候是隐藏一半并半透明显示的。
这里用到一个触摸反馈的方法​​​dispatchTouchEvent​

  • MotionEvent.ACTION_DOWN://手指按下
  • MotionEvent.ACTION_MOVE://手指滑动
  • MotionEvent.ACTION_UP://手指抬起

整体的思路就是在滑动过程中,购物车图标向右位移,并加一个渐变效果。

Android 仿美团悬浮购物车显示隐藏_仿美团悬浮购物车_04

向右移动的距离计算:屏幕的宽度减去图标距左边的宽度(红线),然后加上图标的半径(蓝线)

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
tools:context=".MainActivity">

<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<ImageView
android:id="@+id/iv_cart"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="60dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:contentDescription="@null"
android:src="@drawable/ic_cart"/>

</RelativeLayout>

代码

初始化控件

mListView = findViewById(R.id.list_view);
mIvCart = findViewById(R.id.iv_cart);

初始化数据

for (int i = 0; i < 50; i++) {
titles.add("第 - " + i + " - 条数据");
}
mListView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, titles));

计算移动距离

//控件绘制完成之后再获取其宽高
mIvCart.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//动画移动的距离 屏幕的宽度减去图片距左边的宽度 就是图片距右边的宽度,再加上隐藏的一半
moveDistance = getScreenWidth() - mIvCart.getRight() + mIvCart.getWidth() / 2;
//监听结束之后移除监听事件
mIvCart.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
private int getScreenWidth() {
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}

隐藏动画

private void hideFloatImage(int distance) {
isShowFloatImage = false;

//位移动画
TranslateAnimation ta = new TranslateAnimation(0, distance, 0, 0);
ta.setDuration(300);

//渐变动画
AlphaAnimation al = new AlphaAnimation(1f, 0.5f);
al.setDuration(300);

AnimationSet set = new AnimationSet(true);
//动画完成后不回到原位
set.setFillAfter(true);
set.addAnimation(ta);
set.addAnimation(al);
mIvCart.startAnimation(set);
}

显示动画

private void showFloatImage(int distance) {
isShowFloatImage = true;

//位移动画
TranslateAnimation ta = new TranslateAnimation(distance, 0, 0, 0);
ta.setDuration(300);

//渐变动画
AlphaAnimation al = new AlphaAnimation(0.5f, 1f);
al.setDuration(300);

AnimationSet set = new AnimationSet(true);
//动画完成后不回到原位
set.setFillAfter(true);
set.addAnimation(ta);
set.addAnimation(al);
mIvCart.startAnimation(set);
}

处理滑动逻辑

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://手指按下
if (System.currentTimeMillis() - upTime < 1000) {
//本次按下距离上次的抬起小于1s时,取消Timer
timer.cancel();
}
startY = event.getY();
break;
case MotionEvent.ACTION_MOVE://手指滑动
if (Math.abs(startY - event.getY()) > 10) {
if (isShowFloatImage) {
hideFloatImage(moveDistance);
}
}
startY = event.getY();
break;
case MotionEvent.ACTION_UP://手指抬起
if (!isShowFloatImage) {
//抬起手指1s后再显示悬浮按钮
//开始1s倒计时
upTime = System.currentTimeMillis();
timer = new Timer();
timer.schedule(new FloatTask(), 1000);
}
break;
}
return super.dispatchTouchEvent(event);
}
  • 这里用一个​​upTime​​ 记录手指抬起的时间,如果小于1s动画就不执行,避免快速反复滑动导致动画多次执行。
  • 然后用一个定时器​​timer​​延时执行动画
  • 在手指抬起的时候记录当前时间戳,并执行动画
  • ​moveDistance​​就是计算的移动的距离
  • ​isShowFloatImage​​是一个布尔类型的标识,判断图标状态是否显示
  • startY - event.getY()) > 10 这个大于10是因为手指按下的是一个面,不是一个点,把这个面的高度定为10


github : ​​https://github.com/yechaoa/FloatCartDemo​