效果图:
这是美团的效果
通过效果图可以看到 静止的时候是购物车图标是显示的,滑动的时候是隐藏一半并半透明显示的。
这里用到一个触摸反馈的方法dispatchTouchEvent
- MotionEvent.ACTION_DOWN://手指按下
- MotionEvent.ACTION_MOVE://手指滑动
- MotionEvent.ACTION_UP://手指抬起
整体的思路就是在滑动过程中,购物车图标向右位移,并加一个渐变效果。
向右移动的距离计算:屏幕的宽度减去图标距左边的宽度(红线),然后加上图标的半径(蓝线)
布局
<?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