()

Android仿ios吸边弹簧阻尼效果的移动组件SpringMovingView

  • 功能简介
  • Android技术生活交流
  • Gif演示
  • 实现步骤
  • java代码
  • Android技术生活交流

更多其他页面-自定义View-实用功能合集:点击查看
Github项目地址: 点击跳转,欢迎fork收藏


功能简介

仿ios带有阻尼效果的可自由移动组件。靠近四边有吸附效果,超出四边具有弹簧阻尼效果,超出越多阻力越多,且释放时具有回弹效果。

相关文章讲解:
简单的自由移动组件SimpleMovingView实现 点击浏览

View.layout如何刷新控件位置?点击跳转

用layout方法刷新控件新位置后,为什么页面有新的组件添加后,会刷新页面,控件回到原来的位置 点击跳转



Android技术生活交流

微信 ----- qq群

Gif演示


实现步骤

1.创建SpringMovingView.java并继承一个view
2.对于实现可自由移动组件请看之前的文章如何写的. 点击浏览
3.实现吸边效果. 首先我们先设一个想要的吸附距离mAttach_Distance=20,当组件移动与到屏幕四边的距离<=吸附距离时,视为吸附到四边。立即调用layout(left,top,right,bottom)来刷新组件新位置的UI即可。具体看do_attach_boundary_effect()
4.例如:朝左移动组件,直到当与距离屏幕左边距离<=吸附距离。那么组件left=0,top=不变,right= 左边0+组件宽度,bottom=不变。
5.实现弹簧压缩阻尼,与释放回弹的效果.
6.弹簧压缩阻尼效果:当view随着手指移动超过屏幕四边后,开始减少手指移动赋予view新位置的值,从而达到用手指移动view时感觉像受到了阻碍,具体看do_spring_press_effect()
7.弹簧释放回弹效果:MotionEvent.ACTION_UP手势抬起时,判断下当前组件所处的位置,是否超出了屏幕边界。如果超出了边界,则开始计算超出的部分(长,宽),和将要回弹的高度。具体看do_spring_release_effect()
8.这里主要是调用了位移动画达到实现回弹的效果TranslateAnimation


java代码


import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;/***  作者:游丰泽*  功能介绍:仿ios具有弹簧效果的自由移动组件,靠近四边带有吸附效果。*  移动超出四边具有弹簧阻尼按压效果,且释放后会有弹簧回弹效果,最终落到边界**/public class SpringMovingView extends LinearLayout {private OnClickListener mOnClickListener;//手指按下时相对于屏幕的X,Y位置private float mDownInScreenX, mDownInScreenY;//手指按下时相对于组件的X,Y位置private double mDownInViewX, mDownInViewY;//手指移动产生X,Y的距离private double mMoveDistanceX, mMoveDistanceY;//新位置left top right bottomprivate int mNewPosition_left, mNewPosition_top, mNewPosition_right, mNewPosition_bottom;//屏幕长宽private int mScreenHeight, mScreenWidth;//弹簧释放时,弹起的X,Yprivate double mSpringReleaseX, mSpringReleaseY;//弹簧释放时,动画的起X,终X,起Y,终Yprivate int mAnimStartX=0, mAnimEndX, mAnimStartY=0, mAnimEndY;/*** inner 吸附距离四边的最小距离*/private double mAttach_Distance =20;/*** 弹簧释放时,弹起的高度比例*/private double mSpringReleaseHeightRate =0.5;/*** 动画播放时间*/private int mAnimationDuringTime=300;/*** 动画结束后,组件的位置 left top right bottom*/private   int mAnim_left =0, mAnim_top =0, mAnim_right =0, mAnim_bottom =0;public SpringMovingView(Context mContext) {super(mContext);initView(mContext);}public SpringMovingView(Context mContext, @Nullable AttributeSet attrs) {super(mContext,attrs);initView(mContext);}public SpringMovingView(Context mContext, AttributeSet attrs, int defStyle){super(mContext,attrs,defStyle);initView(mContext);}/*** 初始化View* @param context*/private void initView(final Context context) {DisplayMetrics dm= new DisplayMetrics();WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);wm.getDefaultDisplay().getMetrics( dm );mScreenHeight =dm.heightPixels;mScreenWidth =dm.widthPixels;}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:mDownInViewX =event.getX();mDownInViewY =event.getY();mDownInScreenX = event.getRawX();mDownInScreenY = event.getRawY();break;case MotionEvent.ACTION_MOVE://记录移动距离mMoveDistanceX = event.getX() - mDownInViewX;  //计算手指相对于View移动距离XmMoveDistanceY = event.getY() - mDownInViewY;  //计算手指相对于View移动距离Y//组件新的位置left top right bottommNewPosition_left = (int) (getLeft() + mMoveDistanceX);mNewPosition_right = (int) (getRight() + mMoveDistanceX);mNewPosition_top = (int) (getTop() + mMoveDistanceY);mNewPosition_bottom = (int) (getBottom() + mMoveDistanceY);do_attach_boundary_effect();  //吸边效果 当组件靠近屏幕四边时会有吸附上去的效果do_spring_press_effect();  //弹簧压缩效果 当组件超过屏幕四边后,继续移动会有阻尼效果//防止超出容器边界,而无法进行动画操作if(mNewPosition_left >(-1*getWidth()+1)&& mNewPosition_top >(-1*getHeight()+1)&& mNewPosition_bottom <(mScreenHeight +getHeight()-1)&& mNewPosition_right <(mScreenWidth +getWidth()-1)){refreshNewPosition(mNewPosition_left, mNewPosition_top, mNewPosition_right,  mNewPosition_bottom);}break;case MotionEvent.ACTION_UP:if(getLeft()<0||getRight()> mScreenWidth){ //当左 或 右超出边界时,视为触发了弹簧压缩,则计算弹簧释放后弹出的 X 距离mSpringReleaseX = calculateSpringReleaseX(getLeft(),getRight())* mSpringReleaseHeightRate;}else {mSpringReleaseX=0;}if(getTop()<0||getBottom()> mScreenHeight){ //当上 或 下超出边界时,视为触发了弹簧压缩,则计算弹簧释放后弹出的 Y 距离mSpringReleaseY = calculateSpringReleaseY(getTop(),getBottom())* mSpringReleaseHeightRate;}else {mSpringReleaseY=0;}do_spring_release_effect();   //弹簧回弹效果,当触发了弹簧压缩后,释放手势组件回弹,压缩越多,回弹越多if(null != mOnClickListener) { //当实现了监听click接口if (mDownInScreenX == event.getRawX() && mDownInScreenY == event.getRawY()) { //且view没有移动时,视为点击mOnClickListener.isClick(true);}}break;}return true; //返回true,为了截取手势事件,当前view进行处理}/*** ios弹簧方法-压缩-通过放慢移动速度的方法(超出越多,放慢越多),从而达到压缩的效果**/private  void do_spring_press_effect(){ //ios弹簧方法-压缩if (getLeft() < 0) {  //左放慢mNewPosition_left = (int) (getLeft() + calculateSpringPressDistance(mMoveDistanceX, getLeft()-0));mNewPosition_right = mNewPosition_left + getWidth();} else if (getRight() > mScreenWidth) {   //右放慢mNewPosition_right = (int) (getRight() + calculateSpringPressDistance(mMoveDistanceX, getRight()-mScreenWidth));mNewPosition_left = mNewPosition_right - getWidth();}if (getTop() < 0) {   //上放慢mNewPosition_top = (int) (getTop() + calculateSpringPressDistance(mMoveDistanceY, getTop()-0));mNewPosition_bottom = mNewPosition_top + getHeight();} else if (getBottom() > mScreenHeight) { //下放慢mNewPosition_bottom = (int) (getBottom() + calculateSpringPressDistance(mMoveDistanceY, getBottom()-mScreenHeight));mNewPosition_top = mNewPosition_bottom - getHeight();}}//移动速度随着组件view超出屏幕边界越多,变的越慢private double calculateSpringPressDistance(double distance, double overBoundaryDistance){return  0.01*distance * Math.sqrt(Math.abs(overBoundaryDistance));}//记录一下组件view超出边界的Wprivate double calculateSpringReleaseX(double Left, double Right){double W=0.0;if(Left<0){   //左超边界W= Left;}else if( Left>0 && Right> mScreenWidth)  {  //右超边界W= Right- mScreenWidth;}return W ;}//记录一下组件view超出边界的Hprivate double calculateSpringReleaseY(double Top, double Bottom){double H=0.0;if(Top<0){   //上超边界H= Top;}else if( Top>0 && Bottom> mScreenHeight)  {  //下超边界H= Bottom- mScreenHeight;}return H ;}/*** ios弹簧方法-释放**/private  void do_spring_release_effect() { //ios弹簧方法-释放//当开启弹簧效果,且任意一边超出屏幕边界if(getLeft()<mSpringReleaseX||getRight()> mScreenWidth ||getTop()<mSpringReleaseY||getBottom()> mScreenHeight) {mAnim_left =getLeft(); mAnim_top =getTop();mAnim_right =(int)(mAnim_left +getWidth());   mAnim_bottom = (int) (mAnim_top + getHeight());mAnimEndX =0;mAnimEndY =0;if (getLeft() < mSpringReleaseX) {mAnimEndX = (int) (-1 * mSpringReleaseX * 3);mAnim_left = 0;mAnim_right =(int)(mAnim_left +getWidth());} else if (getRight() > mScreenWidth) {mAnimEndX = (int) ((-1 * mSpringReleaseX * 3));mAnim_left = (int) (mScreenWidth - getWidth());mAnim_right = (int) (mScreenWidth);}if (getTop() < mSpringReleaseY) {mAnimEndY = (int) (-1 * mSpringReleaseY * 3);mAnim_top = 0;mAnim_bottom = (int) (mAnim_top + getHeight());} else if (getBottom() > mScreenHeight) {mAnimEndY = (int) (-1 * mSpringReleaseY * 3);mAnim_bottom = (int) mScreenHeight;mAnim_top = (int) (mAnim_bottom - getHeight());}animation(); //开始进行位移动画}}/*** 回弹位移动画*/private void animation(){TranslateAnimation transAnim = new TranslateAnimation(mAnimStartX, mAnimEndX, mAnimStartY, mAnimEndY);transAnim.setDuration(mAnimationDuringTime);transAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {clearAnimation();refreshNewPosition(mAnim_left, mAnim_top, mAnim_right, mAnim_bottom);}@Overridepublic void onAnimationRepeat(Animation animation) {}});startAnimation(transAnim);}/*** 吸附屏幕方法,当view靠近四边小于设定的距离,且还在屏幕内时触发**/private void do_attach_boundary_effect(){if (mNewPosition_left <= mAttach_Distance && mNewPosition_left > 0) {  //左吸边mNewPosition_left = 0;mNewPosition_right = mNewPosition_left + getWidth();return;} else if (mScreenWidth > mNewPosition_right && mNewPosition_right > mScreenWidth - mAttach_Distance) {  //右吸边mNewPosition_right = mScreenWidth;mNewPosition_left = mNewPosition_right - getWidth();return;}if (mNewPosition_top <= mAttach_Distance && mNewPosition_top > 0) {   //上边吸mNewPosition_top = 0;mNewPosition_bottom = mNewPosition_top + getHeight();return;} else if (mScreenHeight > mNewPosition_bottom && mNewPosition_bottom > mScreenHeight - mAttach_Distance) {  //下边吸mNewPosition_bottom = mScreenHeight;mNewPosition_top = mNewPosition_bottom - getHeight();return;}}//刷新组件新位置UIprivate void refreshNewPosition(int left, int top, int right,int bottom){layout(left,top,right,bottom);}/*** 像外提供监听click方法* @param onClickListener*/public void setOnSpringMovingClickListener(OnClickListener onClickListener){this.mOnClickListener = onClickListener;}/*** 接口*/public interface OnClickListener {void isClick(boolean isClick);}
}

Android技术生活交流

微信 ----- qq群

Android仿IOS吸边弹簧阻尼移动组件SpringMovingView-自定义view系列(3)相关推荐

  1. android rebound平移,Android 仿 IOS 拖拽回弹之进阶 ReboundFrameLayout

    Android 仿 IOS 拖拽回弹之进阶 ReboundFrameLayout 前言 IOS 拖拽回弹给用户的体验不得不赞然后 Android 原生的 API 各种不支持, 于是乎出现的很多仿 IO ...

  2. android仿ios弹框_在“提示”框中:iOS外观(在Android上运行),Google Maps作为Time Machine,下载Wii游戏保存...

    android仿ios弹框 Once a week we round up some great reader tips and share them with everyone. Read on t ...

  3. android 仿ios动画效果代码,Android仿IOS上拉下拉弹性效果的实例代码

    用过iphone的朋友相信都体验过页面上拉下拉有一个弹性的效果,使用起来用户体验很好:Android并没有给我们封装这样一个效果,我们来看下在Android里如何实现这个效果.先看效果,感觉有些时候还 ...

  4. android 密码解锁程序,android 仿ios数字密码解锁界面的实例

    如下所示: 每个Android开发人员都知道,现在android的解锁最常用的就是九宫格解锁,ios的解锁常用的是数字密码解锁.而我们在开发工程中,很多时候,都需要android和ios进行结合.有的 ...

  5. Android仿IOS解锁密码界面-自定义view系列(6)

    Android仿IOS解锁密码界面-自定义view系列 功能简介 主要实现步骤-具体内容看github项目里的代码 xml相关属性设置 Android Studio 代码 Android技术生活交流 ...

  6. android 按钮回弹效果,Android仿IOS回弹效果 支持任何控件

    本文实例为大家分享了Android仿IOS回弹效果的具体代码,供大家参考,具体内容如下 效果图: 导入依赖: dependencies { // ... compile 'me.everything: ...

  7. Android仿IOS滑动关机-自定义view系列(6)

    Android仿IOS滑动关机-自定义view系列 功能简介 GIf演示 主要实现步骤-具体内容看github项目里的代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击 ...

  8. Android安卓仿IOS音量调节-自定义view系列(4)

    Android安卓仿IOS音量调节-自定义view系列 功能简介 主要实现步骤 xml相关属性设置 java代码 Android技术生活交流 更多其他页面-自定义View-实用功能合集:点击查看 Gi ...

  9. android 布局回弹,Android仿IOS回弹效果 支持任何控件

    本文实例为大家分享了Android仿IOS回弹效果的具体代码,供大家参考,具体内容如下 效果图: 导入依赖: dependencies { // ... compile 'me.everything: ...

最新文章

  1. 关于 微软必应词典客户端(pc) 的案例分析
  2. Linux关闭防火墙、SELinux
  3. ie6/IE8/IE9/谷歌以及火狐等浏览器下li 高度一致解决办法
  4. 泛泰SKY新出品 DMB电视手机IM-U160
  5. 《数据库原理与应用》(第三版)第 1 章 数据库概述 习题参考答案
  6. baidu aistudio使用小结
  7. scikit-learn工具包使用建议(转)
  8. linux 中 id指令,Linux id 命令
  9. win10怎么更改账户名称_Win10邮件功能如何查看邮件
  10. 为什么不能把CSS放到html中,为什么我的CSS代码不能在我的HTML文件中工作?
  11. vue-html5-editor接收数据,在vue中获取wangeditor的html和text的操作
  12. python打开chrome浏览器登录用户名密码_[工具]Python获取Chrome浏览器已保存的所有账号密码...
  13. 微信公众号开发 ----微信网页开发config接口注入(3)
  14. HDL4SE:软件工程师学习Verilog语言(十六)
  15. 8脚51单片机DIY时间显示+闹钟技术分享(一)
  16. 如何利用C#/C++调取创蓝253短信验证码
  17. 面试经历深信服面试问题
  18. 【电商支付项目(一)】数据库设计
  19. ABB机器人ProgramEditor(程序编辑器)调试菜单详解
  20. scons的使用方法和进阶

热门文章

  1. vue中域名跨域404的原因
  2. 20221101关于cross_val_score、cross_validate学习
  3. Matlab 小球落地问题
  4. 人工智能Java SDK:文字识别(OCR)工具箱
  5. 正点原子提供免费开源的的连接机智云平台开发教程和实例源码
  6. Wireshark和Fiddler
  7. 云上铺体育场馆管理系统能做什么?
  8. 关于 js 数组遍历的几种方式
  9. 超市会员管理系统(对象+集合)
  10. 一个redis集群的管理工具