自定义ViewGroup动画滑动冲突 事件分发

效果

效果

自定义ViewGroup

package com.example.formwork.view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.GenericLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;

import com.example.formwork.R;

public class TranslateView extends FrameLayout {

    private ImageView translateImg;
    private TextView translateTit;
    private View rootView;

    public TranslateView(@NonNull Context context) {
        super(context);
    }

    public TranslateView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public TranslateView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private int width = 250;//宽度
    private int min = 250;//最小
    private int max = 600;//最大

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    width += 10;//把宽度增大10个长度
                    if (width < max) {//如果宽度大于了max则将控制控件显示出来,并且不再加宽该控件的宽度
                        handler.sendEmptyMessageDelayed(1, 10);//逐渐将宽度增加
                        requestLayout();   //重新调用onMeasure方法,给控件设置新的宽度
                    } else {
                        translateTit.setVisibility(View.VISIBLE);//显现
                    }
                    break;
                case 2:
                    width -= 10;
                    if (width > min) {
                        translateTit.setVisibility(View.INVISIBLE);
                        handler.sendEmptyMessageDelayed(2, 10);
                        requestLayout();
                    }
                    break;
            }
        }
    };

    private void init(Context context, AttributeSet attrs) {
        rootView = LayoutInflater.from(context).inflate(R.layout.translate_view, this);

        translateImg = (ImageView) rootView.findViewById(R.id.translate_img);
        translateTit = (TextView) rootView.findViewById(R.id.translate_tit);


        translateImg.setOnClickListener(view -> {
            handler.removeCallbacksAndMessages(null);
            handler.sendEmptyMessageDelayed(1, 10);
        });

        translateTit.setOnClickListener(view -> {
            Toast.makeText(context, "这是文字", Toast.LENGTH_SHORT).show();
        });

        //通过lifeCycle来监听Activity的生命周期
        LifecycleOwner lifecycleOwner = (LifecycleOwner) context;
        lifecycleOwner.getLifecycle().addObserver(new GenericLifecycleObserver() {
            @Override
            public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {//当页面销毁时,通过该回调可以接收到Lifecycle.Event.ON_DESTROY事件
                    Log.d("LQS", "==页面已经销毁,收到销毁事件");
                    handler.removeCallbacksAndMessages(null);//删除未处理的消息,避免内存泄漏问题
                }
            }
        });
    }

    private int lastY, lastX;

    //继承系统布局,重新写拦截方法,当我们手指在这个控件上滑动的距离大于10,我们就拦截事件,让组件跟随手指滑动,否则的话,就不拦截,让组件里面的控件响应点击事件
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) ev.getRawX();
                lastY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                //当距离差超过10时,就认为是滑动而不是点击了
                if (Math.abs(ev.getRawX() - lastX) > 10 || (Math.abs(ev.getRawY() - lastY) > 10)) {
                    lastX = (int) ev.getRawX();
                    lastY = (int) ev.getRawY();
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    //该方法被执行时,代表是布局已经拦截了事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            int newX = (int) event.getRawX();
            int newY = (int) event.getRawY();

            int dx = newX - lastX;
            int dy = newY - lastY;
//            Log.i("ppppppp2", "onTouchEvent:lastX " + lastX + "--lastX:" + lastY + "--newX:" + newX + "--newY:" + newY + "--dx:" + dx + "--dy:" + dy);
            //改变组件的位置,相当于将组件的左侧,上侧,右侧,下侧根据这个滑动距离偏差都做出改变
            TranslateView.this.layout(TranslateView.this.getLeft()+dx,TranslateView.this.getTop()+dy,
                    TranslateView.this.getRight()+dx,TranslateView.this.getBottom()+dy);

            //将组件改变后的参数保存起来
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(TranslateView.this.width,TranslateView.this.getHeight());
            layoutParams.leftMargin = TranslateView.this.getLeft();
            layoutParams.topMargin = TranslateView.this.getTop();
            layoutParams.rightMargin = TranslateView.this.getRight();
            layoutParams.bottomMargin = TranslateView.this.getBottom();
            layoutParams.setMargins(TranslateView.this.getLeft(),TranslateView.this.getTop(),0,0);
            TranslateView.this.setLayoutParams(layoutParams);//通过该方法,存储到View里


            lastX = newX;
            lastY = newY;
        }
        return true;
    }


    private int listY;
    //判断是否在外部滑动
    public void isResetWidth(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                listY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (event.getRawY() < listY) {//代表你的手指是向上滑动,立刻去告诉自定义View有必要时可以去执行收缩操作,如果该组件处于拉伸状态,就执行收缩
                    resetWidth(event);
                }
                break;
        }

    }

    private void resetWidth(MotionEvent event) {
        //判断一下,当前手指触发的事件,它们的坐标是否在空间上,如果在控件上,不执行收缩操作。只让控件处理点击事件。
        if (isTouchInCurrentView(event)) {
            Log.d("LQS", "在控件上滑动,不需要执行收缩操作");
            return;
        }
        //如果handler已经开始做收缩操作,或者当前控件它的宽度已经是最小了,没必要再做收缩操作
        if (handler.hasMessages(2) || width == 250) {
            return;
        }
        handler.removeCallbacksAndMessages(null);//把之前的消息全部停止,全力做好控件的收缩操作
        handler.sendEmptyMessageDelayed(2, 10);//通过handler自定义收缩动画,完成收缩操作
    }

    private boolean isTouchInCurrentView(MotionEvent event) {
        //获取触摸事件的坐标
        int eventRawX = (int) event.getRawX();
        int eventRawY = (int) event.getRawY();//事件的坐标
        //获取控件的坐标范围
        int[] viewLoc = new int[2];//存放控件的坐标值
        getLocationOnScreen(viewLoc);
        int left = viewLoc[0];//左侧坐标
        int top = viewLoc[1];//顶部坐标
        int right = left + getMeasuredWidth();//右侧坐标
        int bottom = top + getMeasuredHeight();//底部坐标

        boolean isInView = eventRawX >= left && eventRawX <= right &&
                eventRawY >= top && eventRawY <= bottom;
        return isInView;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //设置宽度
        setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec));
    }
}

Activity

  @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        translate.isResetWidth(ev);
        return super.dispatchTouchEvent(ev);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值