Day8-2 自定义ViewGoup+事件分发
一. 自定义ViewGoup
- View 与 ViewGroup 的不同点总结
- 测量: ViewGroup 作为一个容器,他需要去测量子 View 的宽高,打包成他们的期望
- 布局: ViewGroup 要去覆写 onLayout,去布局孩子,调用孩子 layout 方法,指定孩子上下左右的位置
- 绘制: 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.总结三个方法:
-
dispatchTouchEvent:
返回super:调用 子View 的 dispatchTouchEvent,如果没有 子View,调用自己的 onTouchEvent;
返回 true:自己消费事件,并且结束 事件传递,且所有的 onTouchEvent都不会执行(View的、ViewGroup的、Activity的onTouchEvent都不会执行);
返回 false: 自己不处理事件,结束事件传递,不调用自己的 onTouchvent,而是直接调用 父控件的 onTouchEvent; -
onInterceptTouchEvent:
返回true:自己拦截事件,然后调用自己的 onTouchEvent,不会向下传递事件;
返回super/false:自己不拦截事件,继续向下传递事件; -
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);
}
}