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

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

先看效果

这个是完成的效果,还可以吧!关键也不难一个自定义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);
    }

    @Override
    public 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;
    }

    @Override
    protected 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;
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.d("VipCardViewScllorView", "onLayout====");
        cardViewOnClick();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d("VipCardViewScllorView", "onDraw====");
    }

    @Override
    public 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, int
            animationTime) {
        final TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, fromYDelta, toYDelta);
        translateAnimation.setDuration(animationTime);
        translateAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                Log.d("onAnimationEnd", "getRootView()1===" + layoutParams_up.topMargin);
                isScroll = false;
            }

            @Override
            public 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;
            }

            @Override
            public 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, int
            animationTime) {
        final TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, fromYDelta, toYDelta);
        translateAnimation.setDuration(animationTime);
        translateAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                translateAnimation.cancel();//解决界面闪动的关键代码
                layoutParams_up.setMargins(0, top, 0, 0);
                view.setLayoutParams(layoutParams_up);
                view.clearAnimation();
                individual_layout.clearAnimation();


            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        view.startAnimation(translateAnimation);
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public 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() {
                    @Override
                    public 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布局和自定义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)