本来不想自己造轮子的,但奈何没找动相应效果的轮子,所以只能自己写了,其实还是白嫖来的轻松,哈哈

先看效果

这个是完成的效果,还可以吧!关键也不难一个自定义View搞定

先说一下思路,继承一个RelativeLayout  在布局中加入两个卡片的位置View,至于里面放什么可以随意扩展,复写view的onTouchEvent方法以及onInterceptTouchEvent方法 ,onTouchEvent方法只是单纯的去区分手势的滑动处理,而onInterceptTouchEvent是为了对卡片页面进行操作的时候处理

onTouchEvent方法下需要记录按下的坐标点以及滑动距离,通过滑动距离的改变来给卡片的view设置topMargin,来达到让卡片进行移动的效果,当然了  如果仅仅是设置topMargin的方式还不能满足,还要对移动过程增加平移动画TranslateAnimation,这样就看起来更加流畅

看一下代码

package view;import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;
import cc.vv.scoring.mine.R;//会员卡滑动效果
public class VipCardViewScllorView extends RelativeLayout implements View.OnClickListener {/*** 切换过程是否完成*/private boolean isSwitch = true;/*** 滑动过程中不能点击切换*/private boolean isScroll = true;/*** 是否展开了*/private boolean isOpen = false;/*** 是否第一次进来*/private boolean isFirst = true;/*** 触摸的起始Y坐标*/private int startY;/*** 滑动Y距离*/private int movedY;/*** 最终距离*/private int offsetY;private Context context;/*** 一进来初始的margeTop值*/private int startMargeTop = 0;//卡片总布局private RelativeLayout card_content;//底部布局private RelativeLayout individual_layout;//公司会员卡public RelativeLayout card_view_company;//个人会员卡public RelativeLayout card_view_personal;private Handler handler;public VipCardViewScllorView(Context context) {super(context);this.context = context;}public VipCardViewScllorView(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;}public VipCardViewScllorView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.context = context;}public void initView(Handler handler) {this.handler = handler;removeAllViews();View view = LayoutInflater.from(context).inflate(R.layout.view_vipcard_layout, null, false);card_content = view.findViewById(R.id.card_content);card_view_company = view.findViewById(R.id.card_view_company);individual_layout = view.findViewById(R.id.individual_layout);card_view_personal = view.findViewById(R.id.card_view_personal);RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);addView(view, layoutParams);}//会员卡布局的优先级//这个逻辑不用管是我的业务逻辑public void vipLayoutisSwitch(int type) {if (type == 2) {//公司卡在前final View childAt_OnClick = card_content.getChildAt(1);final View childAt0_OnClick = card_content.getChildAt(0);final View bot = card_content.getChildAt(2);card_content.removeAllViews();card_content.addView(childAt_OnClick, 0);card_content.addView(childAt0_OnClick, 1);card_content.addView(bot, 2);card_content.requestLayout();}}public void cardViewOnClick() {card_content.getChildAt(0).setOnClickListener(this);card_content.getChildAt(1).setOnClickListener(this);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startY = (int) event.getRawY();//获得按下时的Y坐标Log.d("zgcMotionEvent", "startY===" + startY);Log.d("zgcMotionEvent", "starttop===" + startMargeTop);Log.d("zgcMotionEvent", "card_content===" + card_content.getMeasuredHeight());if (startY - (startMargeTop * 2) >= card_content.getMeasuredHeight()) {//把滑动区域只放在卡片上而不是整个页面Log.d("zgcMotionEvent", "===条件不符合,拦截点击事件===");return false;}break;case MotionEvent.ACTION_MOVE:movedY = (int) event.getRawY();//获得移动时候的Y坐标//当手指移动的时候View childAt_MOVE = card_content.getChildAt(1);View childAt0_MOVE = card_content.getChildAt(0);LayoutParams layoutParams_MOVE = (LayoutParams) childAt_MOVE.getLayoutParams();offsetY = startY - movedY;//获得Y轴的偏移量Log.d("zgcOffsetY", "offsetY====" + offsetY);if (isOpen == false && Math.abs(offsetY) > startMargeTop && offsetY < 0) {if (Math.abs(offsetY) >= childAt0_MOVE.getMeasuredHeight()) {layoutParams_MOVE.topMargin = childAt0_MOVE.getMeasuredHeight();childAt_MOVE.setLayoutParams(layoutParams_MOVE);Log.d("ACTION_MOVE", "==滑动已经到达最大位置  设置展开=");} else {layoutParams_MOVE.topMargin = Math.abs(offsetY);childAt_MOVE.setLayoutParams(layoutParams_MOVE);Log.d("ACTION_MOVE", "==滑动没有到达最大位置  继续增加topMargin");}} else if (offsetY > 0 && isOpen == true) {if (Math.abs(offsetY - childAt0_MOVE.getMeasuredHeight()) <= startMargeTop) {layoutParams_MOVE.topMargin = startMargeTop;childAt_MOVE.setLayoutParams(layoutParams_MOVE);Log.d("ACTION_MOVE", "==是收缩的并且继续向上滑动");} else {if (childAt0_MOVE.getMeasuredHeight() - offsetY > 0) {layoutParams_MOVE.topMargin = childAt0_MOVE.getMeasuredHeight() - offsetY;childAt_MOVE.setLayoutParams(layoutParams_MOVE);Log.d("ACTION_MOVE", "==没有收缩的并且继续向上滑动");}}}break;case MotionEvent.ACTION_UP:final View childAt_UP = card_content.getChildAt(1);final View childAt0_UP = card_content.getChildAt(0);final LayoutParams layoutParams_up = (LayoutParams) childAt_UP.getLayoutParams();Log.d("zgcOffsetY", "offsetY====" + offsetY);//收缩if (isOpen == true) {if (offsetY + startMargeTop >= childAt_UP.getMeasuredHeight() / 2) {setTranslateAnimation(childAt_UP, layoutParams_up, 0, startMargeTop - layoutParams_up.topMargin, startMargeTop, 350);isOpen = false;Log.d("TranslateAnimation", "==滑动已经过半  卡片收缩");} else if (Math.abs(offsetY) >= childAt_UP.getMeasuredHeight()) {layoutParams_up.setMargins(0, childAt_UP.getMeasuredHeight(), 0, 0);childAt_UP.setLayoutParams(layoutParams_up);Log.d("TranslateAnimation", "=处于展开状态  状态下直接收缩");isOpen = true;} else {setTranslateAnimation(childAt_UP, layoutParams_up, 0, childAt0_UP.getMeasuredHeight() - layoutParams_up.topMargin, childAt0_UP.getMeasuredHeight(), 350);isOpen = true;Log.d("TranslateAnimation", "==滑动没有过半  卡片继续展开");}} else if (isOpen == false) {//展开if (layoutParams_up.topMargin >= childAt_UP.getMeasuredHeight() / 2) {Log.d("TranslateAnimation", "==滑动过半  卡片慢慢展开" + (childAt0_UP.getMeasuredHeight() - layoutParams_up.topMargin - startMargeTop));setTranslateAnimation(childAt_UP, layoutParams_up, 0, childAt0_UP.getMeasuredHeight() - layoutParams_up.topMargin, childAt_UP.getMeasuredHeight(), 350);isOpen = true;} else {setTranslateAnimation(childAt_UP, layoutParams_up, 0, -(layoutParams_up.topMargin - startMargeTop), startMargeTop, 350);isOpen = false;Log.d("TranslateAnimation", "==滑动没有过半  卡片继续收缩");}}break;}return true;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.d("VipCardViewScllorView", "onMeasureY====");if (isFirst) {View childAt = card_content.getChildAt(1);RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) childAt.getLayoutParams();startMargeTop = layoutParams.topMargin;isFirst = false;}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);Log.d("VipCardViewScllorView", "onLayout====");cardViewOnClick();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.d("VipCardViewScllorView", "onDraw====");}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:startY = (int) event.getRawY();//获得按下时的Y坐标break;case MotionEvent.ACTION_MOVE:movedY = (int) event.getRawY();//获得移动时候的Y坐标if (isOpen == true) {if (startY - movedY == 0) {return false;}} else {if (startY - movedY == 0 && isOpen == false) {Log.d("zgcACTION", "ACTION_MOVE===" + (startY - movedY));return false;}}return true;}return false;}//设置滑动动画public void setTranslateAnimation(final View view, final LayoutParams layoutParams_up, float fromYDelta, final float toYDelta, final int top, intanimationTime) {final TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, fromYDelta, toYDelta);translateAnimation.setDuration(animationTime);translateAnimation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {Log.d("onAnimationEnd", "getRootView()1===" + layoutParams_up.topMargin);isScroll = false;}@Overridepublic void onAnimationEnd(Animation animation) {Log.d("onAnimationEnd", "top" + top);translateAnimation.cancel();//解决界面闪动的关键代码layoutParams_up.setMargins(0, top, 0, 0);view.setLayoutParams(layoutParams_up);view.clearAnimation();individual_layout.clearAnimation();Log.d("onAnimationEnd", "getRootView()2===" + layoutParams_up.topMargin);isScroll = true;}@Overridepublic void onAnimationRepeat(Animation animation) {}});view.startAnimation(translateAnimation);individual_layout.startAnimation(translateAnimation);}//设置切换动画public void setSwitchAnimation(final View view, final LayoutParams layoutParams_up, float fromYDelta, float toYDelta, final int top, intanimationTime) {final TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, fromYDelta, toYDelta);translateAnimation.setDuration(animationTime);translateAnimation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {translateAnimation.cancel();//解决界面闪动的关键代码layoutParams_up.setMargins(0, top, 0, 0);view.setLayoutParams(layoutParams_up);view.clearAnimation();individual_layout.clearAnimation();}@Overridepublic void onAnimationRepeat(Animation animation) {}});view.startAnimation(translateAnimation);}@RequiresApi(api = Build.VERSION_CODES.N)@Overridepublic void onClick(final View v) {//在收缩状态下点击 卡片切换位置final View childAt_OnClick = card_content.getChildAt(1);final View childAt0_OnClick = card_content.getChildAt(0);final View bot = card_content.getChildAt(2);float translationY = bot.getTranslationY();float translationX = bot.getTranslationX();if (v.getId() == card_content.getChildAt(0).getId()) {if (isOpen == false && isSwitch == true && isScroll == true) {isSwitch = false;card_content.removeAllViews();card_content.addView(childAt_OnClick, 0);card_content.addView(childAt0_OnClick, 1);card_content.addView(bot, 2);View childAt = card_content.getChildAt(0);View childAt0 = card_content.getChildAt(1);View childAt1 = card_content.getChildAt(2);childAt1.setTranslationY(translationY);childAt1.setTranslationX(translationX);LayoutParams layoutParams = (LayoutParams) childAt.getLayoutParams();LayoutParams childAt0Params = (LayoutParams) childAt0.getLayoutParams();setSwitchAnimation(childAt0, childAt0Params, 0, startMargeTop, startMargeTop, 500);setSwitchAnimation(childAt, layoutParams, startMargeTop, -startMargeTop, 0, 500);handler.post(new Runnable() {@Overridepublic void run() {if (v.getId() == R.id.card_view_company) {((RelativeLayout.LayoutParams) bot.getLayoutParams()).addRule(RelativeLayout.BELOW, R.id.card_view_company);}if (v.getId() == R.id.card_view_personal) {((RelativeLayout.LayoutParams) bot.getLayoutParams()).addRule(RelativeLayout.BELOW, R.id.card_view_personal);}}});isSwitch = true;}}}
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_FFFFFF"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_FFFFFF"android:id="@+id/rootView"android:orientation="vertical"><RelativeLayoutandroid:id="@+id/card_content"android:layout_width="match_parent"android:layout_height="match_parent"><RelativeLayoutandroid:id="@+id/card_view_company"android:layout_width="match_parent"android:layout_height="@dimen/lk_dp_210"></RelativeLayout><RelativeLayoutandroid:id="@+id/card_view_personal"android:layout_width="match_parent"android:layout_height="@dimen/lk_dp_210"android:layout_marginTop="@dimen/dp_40"></RelativeLayout><RelativeLayoutandroid:id="@+id/individual_layout"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_FFFFFF"android:layout_below="@+id/card_view_personal"android:layout_marginTop="@dimen/dp_20"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="会员特权"android:textColor="@color/color_232636"android:textSize="@dimen/sp_18" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="@dimen/dp_30"android:background="@color/color_FFFFFF"android:orientation="horizontal"android:padding="@dimen/dp_5"><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"><cc.vv.baselibrary.view.LKCircleImageViewandroid:id="@+id/personal_jishu"android:layout_width="@dimen/dp_80"android:layout_height="@dimen/dp_80"android:layout_centerHorizontal="true"android:src="@mipmap/vip_center_icon_jishu" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/personal_jishu"android:layout_centerHorizontal="true"android:layout_marginTop="@dimen/dp_14"android:text="个人技术统计"android:textColor="@color/color_5C5C66" /></RelativeLayout><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"><cc.vv.baselibrary.view.LKCircleImageViewandroid:id="@+id/vip_youhui"android:layout_width="@dimen/dp_80"android:layout_height="@dimen/dp_80"android:layout_centerHorizontal="true"android:src="@mipmap/vip_center_icon_youhui" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/vip_youhui"android:layout_centerHorizontal="true"android:layout_marginTop="@dimen/dp_14"android:text="会员优惠"android:textColor="@color/color_5C5C66" /></RelativeLayout><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"><cc.vv.baselibrary.view.LKCircleImageViewandroid:id="@+id/wuzhangai_in"android:layout_width="@dimen/dp_80"android:layout_height="@dimen/dp_80"android:layout_centerHorizontal="true"android:src="@mipmap/vip_center_icon_jinchang" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/wuzhangai_in"android:layout_centerHorizontal="true"android:layout_marginTop="@dimen/dp_14"android:text="无障碍进场"android:textColor="@color/color_5C5C66" /></RelativeLayout></LinearLayout></RelativeLayout></RelativeLayout></LinearLayout>
</LinearLayout>

这就是全部代码了  静态xml布局和自定义View代码

其中大部分的代码主要集中在了onTouchEvent的 ACTION_DOWN(手指按下)、ACTION_MOVE(手指滑动)、ACTION_UP(手指离开)、这三个手势监听上面,当手指按下(ACTION_DOWN)时记录当前坐标点Y(因为咱们是上下滑动,所以只有Y坐标会变x坐标是不会改变的),当手指开始滑动(ACTION_MOVE)我们把滑动距离设置给当前第一层View的topmargin,当然必须设置最大值不然滑的看不见影子了,当滑动距离大于等于最大值时 我们直接设置topmargin最大,这个时候千万不要设置break事件,如果加入了break终止事件当View的topmargin大于等于最大值时View就滑动停止了,所以想让view在手指不离开前随手指上下滑动就不要乱动

当手指离开时(ACTION_UP)这个时候需要判断一下滑动处于什么位置,我这里是判断滑动距离小于一半就自动回弹上去,大于了才展开,收缩的时候也是一样,这样上下滑动回弹效果就完成了,忘记说了回弹和展开统一使用的是平移动画TranslateAnimation

当然还有一个前后切换的动画效果也是TranslateAnimation,原理就是当点击的view是最底下的View时我们先把它换到第一层然后给他设置一个向下的平移动画,原来第一层的View换到最底层给他设置一个向上的平移动画,这样看起来就仿佛两个在前后切换一样

代码的注释 我已经写得很详细了,使用的话就是xml布局初始化  然后调用view里面的 initView方法

android卡包动画,自定义View实现银行卡卡包动画效果相关推荐

  1. android 仿支付宝动画,自定义view之仿支付宝动画

    在学习本篇之前,你首先需要了解如下知识点: 自定义属性 实现效果图: 支付宝动画.gif 实现步骤: 绘制圆形 绘制对号的左边部分 绘制对号右边的部分 是不是感觉说了和没说一样,但思路就是这样 我们先 ...

  2. android 自定义 对号,Android自定义View实现打钩动画功能

    先上效果图 动图 静态图 1. 回顾 [Android自定义View:一个精致的打钩小动画]上一篇文章,我们已经实现了基本上实现了控件的效果了,但是...但是...过了三四天后,仔细看回自己写的代码, ...

  3. Android模仿淘宝语音输入条形动画,录音动画自定义View

    Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...

  4. android view显示隐藏动画效果,Android 根据手势顶部View自动展示与隐藏效果

    首先来看一下效果: 大体思路如下: 总体布局用了一个自定义的ViewGroup,里面包了两个View(top View,bottomView) 我在bottomView里放了ViewPager,里面又 ...

  5. android动画view上移,在Android开发中使用View制作一个引导动画

    在Android开发中使用View制作一个引导动画 发布时间:2020-11-20 16:46:16 来源:亿速云 阅读:98 作者:Leah 这篇文章将为大家详细讲解有关在Android开发中使用V ...

  6. 安卓自定义view仿小米商城购物车动画

    通过自定义View与ViewGroup实现小米商城购物车效果 用到的知识点 自定义View 自定义ViewGroup 贝塞尔曲线 原理 通过贝塞尔曲线实现商品抛入购物车的路径 自定义ViewGroup ...

  7. Android中的自定义View以及绘图工具

    1.1自定义view的简介 为什么要使用自定义view 在Android开发中有很多业务场景,原生的控件是无法满足应用,并且经常也会遇到一个UI在多处 重复使用情况,那么就需要通过自定义View的方式 ...

  8. android标尺自定义view,android尺子的自定义view——RulerView详解

    项目中用到自定义尺子的样式: 原效果为 因为跟自己要使用的view稍有不同 所以做了一些修改,修改的注释都放在代码中了,特此记录一下. 首先是一个自定义View: public class RuleV ...

  9. Android进阶之自定义View实战(二)九宫格手势解锁实现

    一.引言 在上篇博客Android进阶之自定义View实战(一)仿iOS UISwitch控件实现中我们主要介绍了自定义View的最基本的实现方法.作为自定义View的入门篇,仅仅介绍了Canvas的 ...

最新文章

  1. registry ---------仓库 -----------------镜像
  2. struts.xml web.xml配置正常,访问action时出现404
  3. Pytorch交叉熵损失函数torch.nn.functional as F
  4. python pandas csv时间聚合_Python通过pandas操作excel常用功能
  5. python中hashset_python中的集合
  6. wordpress 后台慢_建站经验-wordpress用户注册收不到验证邮件
  7. 【大数据系列】hadoop单节点安装官方文档翻译
  8. c 如何操作php,thinkphp的c方法使用示例
  9. vim 基本操作总结
  10. Ubuntu 14.04 配置iptables防火墙
  11. 网站服务器开启cookies,浏览器如何开启cookie(图解浏览器cookie功能使用)
  12. 大华电子秤 手动变价方法
  13. java房屋租赁系统-房东租客系统PHP小程序
  14. “New”一个完美对象,再来好好面向对象
  15. 教你如何在Windows XP使用定时关机命令
  16. 详细介绍文本检索基准BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models
  17. 学硕停招,985高校这些专业开始只留专硕
  18. 黑苹果macOS机型对照表
  19. html存储数据的方法,数据存储方式有哪些
  20. python locust api_性能测试工具--Locust官方文档(API)解读(全)

热门文章

  1. geoserver 黑白相间铁路样式文件
  2. JWT --- 入门学习
  3. [浪子学编程][读书笔记]-道法自然之用例分析
  4. Instrumentation
  5. 如何确认一个期刊是否是SCI期刊?
  6. Java之GUI编程学习笔记六 —— AWT相关(画笔paint、鼠标监听事件、模拟画图工具)
  7. 浏览器onbeforeunload
  8. 笛卡尔乘积与数据库连接(join)
  9. HTML中click()和onclick()的本质区别与案例和解释
  10. 字符串拼接onclick函数