Day8-2 自定义ViewGoup+事件分发

一. 自定义ViewGoup

  • View 与 ViewGroup 的不同点总结
  1. 测量: ViewGroup 作为一个容器,他需要去测量子 View 的宽高,打包成他们的期望
  2. 布局: ViewGroup 要去覆写 onLayout,去布局孩子,调用孩子 layout 方法,指定孩子上下左右的位置
  3. 绘制: ViewGroup 一般不绘制自己

二.自定义ViewGoup:简单理解

在这里插入图片描述

1.activity布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <com.bawei.day8_view.MyViewGroup
        android:background="#000"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:text="子"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"></Button>
    </com.bawei.day8_view.MyViewGroup>

</LinearLayout>

2.自定义ViewGroup
注意:如果onLayout中什么都不写,那么子控件将不显示

public class MyViewGroup extends ViewGroup {
    public MyViewGroup(Context context) {
        super(context);
    }

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    //摆放:通过子控件的layout(左 上 右 下)摆放
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        int childCount = getChildCount();//获得子控件的个数
        for (int j= 0;j<childCount;j++){//遍历每个子控件
            View view = getChildAt(j);//获得子控件
            i = 300;//左
            i1 = 300;//上
            i2 = 400;//右
            i3 = 400;//下
            view.layout(i,i1,i2,i3);//摆放
        }
    }
}

三.自定义RelativeLayout案例:视频点赞

public class MyHeatView extends RelativeLayout {
    public MyHeatView(Context context) {
        super(context);
    }

    public MyHeatView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            float x = event.getX();
            float y = event.getY();
            //创建ImageView并设置出现位置
            ImageView imageView = new ImageView(getContext());
            imageView.setImageResource(R.drawable.love);
            LayoutParams layoutParams = new LayoutParams(200, 200);
            layoutParams.leftMargin = (int) (x-100);
            layoutParams.topMargin = (int) (y-100);
            imageView.setLayoutParams(layoutParams);
            //动画集合
            AnimatorSet animatorSet = new AnimatorSet();
            ObjectAnimator translationY = ObjectAnimator.ofFloat(imageView, "translationY", 0, -200);
            ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1, 0);
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 1, 2);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(imageView, "scaleY", 1, 2);
            ObjectAnimator rotation = ObjectAnimator.ofFloat(imageView, "rotation", 0, 30);
            rotation.setInterpolator(new CycleInterpolator(3));
            animatorSet.setDuration(500)
                    .play(translationY)
                    .with(alpha)
                    .with(scaleX)
                    .with(scaleY);
            animatorSet.start();
            //向容器中添加imageview
            addView(imageView);

        }
        return true;
    }
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:background="#000"
    android:layout_height="match_parent"
    tools:context=".mvp.view.activity.VideoActivity">
    <VideoView
        android:layout_centerInParent="true"
        android:id="@+id/vv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></VideoView>
    <com.bawei.lib_core.widget.MyHeatView
        android:id="@+id/heart"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></com.bawei.lib_core.widget.MyHeatView>
</RelativeLayout>

四.事件分发机制

1.为什么要事件分发?

2个ScrollView嵌套的时候,滑动子Scrollview失效,粘贴代码,牛刀小试下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".shijian.Main3Activity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <ScrollView
                android:id="@+id/sr"
                android:layout_width="match_parent"
                android:layout_height="300dp">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="100dp"
                        android:background="#aaa"
                        android:text="对对对" />
                </LinearLayout>
            </ScrollView>


            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />


            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#faa"
                android:text="对对对" />
        </LinearLayout>
    </ScrollView>



</LinearLayout>

2.被分发的对象

在这里插入图片描述

3.分发事件的组件

在这里插入图片描述

4.分发的核心方法

在这里插入图片描述

5.事件分发过程

红色主线U形图
在这里插入图片描述

6.总结三个方法:

  1. dispatchTouchEvent:
    返回super:调用 子View 的 dispatchTouchEvent,如果没有 子View,调用自己的 onTouchEvent;
    返回 true:自己消费事件,并且结束 事件传递,且所有的 onTouchEvent都不会执行(View的、ViewGroup的、Activity的onTouchEvent都不会执行);
    返回 false: 自己不处理事件,结束事件传递,不调用自己的 onTouchvent,而是直接调用 父控件的 onTouchEvent;

  2. onInterceptTouchEvent:
    返回true:自己拦截事件,然后调用自己的 onTouchEvent,不会向下传递事件;
    返回super/false:自己不拦截事件,继续向下传递事件;

  3. onTouchEvent:
    返回true:自己消费事件,事件结束 传递;
    返回super/false:自己不处理事件,事件向上传递;

7.牛刀小试分发过程

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".shijian.Main2Activity">
    <com.bawei.day8_view.shijian.OneView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.bawei.day8_view.shijian.TwoView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </com.bawei.day8_view.shijian.TwoView>
    </com.bawei.day8_view.shijian.OneView>

activity代码:

public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
    }
    //分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(ev.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "Main2Activity:dispatchTouchEvent: ");
        }
        return super.dispatchTouchEvent(ev);
    }
    //触摸
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "Main2Activity:onTouchEvent: ");
        }
        return super.onTouchEvent(event);
    }
}

OneView代码:


public class OneView extends LinearLayout {
    public OneView(Context context) {
        super(context);
    }

    public OneView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(ev.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "OneView:dispatchTouchEvent: ");
        }
        return super.dispatchTouchEvent(ev);
    }
    //触摸

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "OneView:onTouchEvent: ");
        }
        return super.onTouchEvent(event);
    }
    //拦截
    //返回true:自己消费事件结束,然后调用自己的 onTouchEvent,不会向下传递事件
    //返回super/false:自己不拦截事件,继续向下传递事件
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(ev.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "OneView:onInterceptTouchEvent: ");
        }
        return super.onInterceptTouchEvent(ev);
    }
}

TwoView代码:

public class TwoView extends View {
    public TwoView(Context context) {
        super(context);
    }

    public TwoView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    //分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(ev.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "TwoView:dispatchTouchEvent: ");
        }
        return super.dispatchTouchEvent(ev);
    }
    //触摸
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() ==MotionEvent.ACTION_DOWN) {
            Log.d("ytx", "TwoView:onTouchEvent: ");
        }

        return super.onTouchEvent(event);
    }
}

默认打印过程:
在这里插入图片描述
oneView拦截事件并自己消费该事件
在这里插入图片描述
在这里插入图片描述

8.解决scrollview嵌套冲突

(1)冲突原因:ScrollView的源码拦截事件,导致内部ScrollView无法响应事件

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        
        /*
        * Shortcut the most recurring case: the user is in the dragging
        * state and he is moving his finger.  We want to intercept this
        * motion.
        * 当用户手指在屏幕上处于滑动状态时,拦截这个动作。
        */
        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }
 
        /*...*/
}

(2)冲突原因:外部ScrollView不拦截此事件

在这里插入图片描述

简单写法:

public class MyScrollView extends ScrollView {
 
    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    	//不拦截
        return false;
    }
}

复杂写法:

public class MyScrollView extends ScrollView {
    private int mLastX, mLastY;
    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            //down事件时,不拦截
            case MotionEvent.ACTION_DOWN: {
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                break;
            }
            //move事件时,拦截
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    return true;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }

        return super.onInterceptTouchEvent(event);

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值