前言
View事件分發
在Android中,View無處不在,不管是一個普通的視圖,還是一個復雜的布局,都是依靠View來實作,而View中的事件分發和處理,是其核心知識點之一,也是開發者學習View時的難點,
在分析事件處理前,我們需要明白,android中一個完整的手勢(gesture)包括如下4個操作:
DOWN: 當用戶手指按下時
MOVE: 當用戶開始滑動時
UP: 用戶抬起手指
CANCEL: 取消操作,事件被無法到達時
任何一個手勢都需要從DOWN開始,
在事件分發中,我們需要區分View和ViewGroup,雖然后者也是繼承與View,但是ViewGroup重寫了dispatchTouchEvent()方法,同時,也只有在ViewGroup會處理onInterceptTouchEvent()方法,而一個事件分發程序,是從一個ViewGroup開始,在這程序中我們需要了解以下三個重要的方法:
dispatchTouchEvent()
onInterceptTouchEvent()
onTouchEvent()
注意:子View沒有 onInterceptTouchEvent,因為 子View已經是最底層了,不用在向下傳遞,只有 ViewGroup 有 onInterceptTouchEvent方法;
總結:
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:自己不處理事件,事件向上傳遞;
圖例執行說明:

方法說明
//分發
//super.dispatchTouchEvent(ev) --> 將事件向上傳遞 --> 交給父容器 處理是否分發
//true --> 不向下分發 --> 不向上傳遞 --> 我自己來處理所有事件
//false --> 不向下分發 --> 向上傳遞 --> 完成父容器的事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("GAO", "CustomView-dispatchTouchEvent");
return false;
}
//攔截
//return true 進行攔截 --> 不向下給子控制元件傳遞事件
//return false 不進行攔截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("GAO", "CustomView-onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
//所有觸摸事件
//按下
//移動
//抬起
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("GAO", "CustomView-onTouchEvent");
return super.onTouchEvent(event);
}
解說
a. 對于 dispatchTouchEvent,onTouchEvent,return true是終結事件傳遞,return false 是回溯到父View的onTouchEvent方法,
b. ViewGroup 想把自己分發給自己的onTouchEvent,需要攔截器onInterceptTouchEvent方法return true 把事件攔截下來,
c. ViewGroup 的攔截器onInterceptTouchEvent 默認是不攔截的,所以return super.onInterceptTouchEvent()=return false;
d. View 沒有攔截器,為了讓View可以把事件分發給自己的onTouchEvent,View的dispatchTouchEvent默認實作(super)就是把事件分發給自己的onTouchEvent,
下拉重繪實際實際案例
package com.fenghongzhang.exam;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class FragmentRefreshLayout extends FrameLayout {
private LinearLayout mRefreshLayout;
private int mMaxRefreshHeight = 200;
private PointF mBeforePointF;
public FragmentRefreshLayout(@NonNull Context context) {
super(context);
initView();
initData();
}
public FragmentRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
initData();
}
private void initView() {
mRefreshLayout = new LinearLayout(getContext());
LayoutParams refreshParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, mMaxRefreshHeight);
refreshParams.topMargin = -mMaxRefreshHeight;
mRefreshLayout.setLayoutParams(refreshParams);
mRefreshLayout.setBackgroundColor(Color.RED);
addView(mRefreshLayout);
}
private void initData() {
mBeforePointF = new PointF();
}
//分發事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mBeforePointF.x = ev.getX();
mBeforePointF.y = ev.getY();
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
//偏移量
float xDimension = Math.abs(ev.getX() - mBeforePointF.x);
float yDimension = Math.abs(ev.getY() - mBeforePointF.y);
//判讀滑動方向
if (xDimension > yDimension * 2) {
//X軸滑動
return super.dispatchTouchEvent(ev);
}
//最小回應距離
if (yDimension < 5) {
return super.dispatchTouchEvent(ev);
}
//可滑動范圍
//Main下拉
FrameLayout.LayoutParams mainParams = (LayoutParams) mRefreshLayout.getLayoutParams();
mainParams.topMargin += ev.getY() - mBeforePointF.y;
if (mainParams.topMargin > 0) {
mainParams.topMargin = 0;
}
//
if (mainParams.topMargin < -mMaxRefreshHeight) {
mainParams.topMargin = -mMaxRefreshHeight;
}
mRefreshLayout.setLayoutParams(mainParams);
mBeforePointF.x = ev.getX();
mBeforePointF.y = ev.getY();
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
//當滑動位置,打開或關閉
LayoutParams mainParams = (LayoutParams) mRefreshLayout.getLayoutParams();
if (mainParams.topMargin < -mMaxRefreshHeight / 2 ) {
startAnim(mainParams.topMargin, -mMaxRefreshHeight);
} else {
startAnim(mainParams.topMargin, 0);
}
}
return true;
}
private void startAnim(int start, int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
valueAnimator.setDuration(500);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
FrameLayout.LayoutParams mainParams = (LayoutParams) mRefreshLayout.getLayoutParams();
mainParams.topMargin = (int) animation.getAnimatedValue();
mRefreshLayout.setLayoutParams(mainParams);
// requestLayout();
}
});
valueAnimator.start();
}
}
實體滑動沖突
https://www.jianshu.com/p/982a83271327
布局
<?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=".MainActivity">
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ss"
android:scrollbars="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="texttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttext"
android:textSize="50dp"
android:id="@+id/text"></TextView>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/lv"></ListView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="texttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttext"
android:textSize="50dp"
android:id="@+id/text1"></TextView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="texttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttexttext"
android:textSize="50dp"
android:id="@+id/text2"></TextView>
</LinearLayout>
</ScrollView>
</LinearLayout>
代碼
package com.fenghongzhang.test;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ListView lv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
List<String> list = new ArrayList<>();
list.add("diyige1");
list.add("diyige2");
list.add("diyige3");
list.add("diyige4");
list.add("diyige");
list.add("diyige");
list.add("diyige");
list.add("diyige");
list.add("diyige");
list.add("diyige");
//解決listView的高度問題
ViewGroup.LayoutParams layoutParams = lv.getLayoutParams();
layoutParams.height = 300;
lv.setLayoutParams(layoutParams);
lv.setAdapter(new MyAdapter(list,this));
lv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//不允許父層攔截或干擾本控制元件
lv.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/264518.html
標籤:其他
上一篇:仿QQ計步器效果的實作
