效果图:
这是美团的效果
通过效果图可以看到 静止的时候是购物车图标是显示的,滑动的时候是隐藏一半并半透明显示的。
这里用到一个触摸反馈的方法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
ok,完成,借鉴https://blog.csdn.net/Lindroid20/article/details/78887347