面試十二連問裆馒,你招的住嗎?
1.事件分發(fā)機(jī)制是怎么樣的丐怯?
2.onTouch和onTouchevent和onClick的執(zhí)行順序?
3.onTouch返回值喷好,onTouchevent返回值,導(dǎo)致結(jié)果如何,添加linsternr又如何?
4.Button和ImageView有什么不一樣?
5.如何理解消費(fèi)响逢?
6.可以達(dá)到父控件和子空間同時(shí)點(diǎn)擊嗎绒窑?
7.ListView上的button,點(diǎn)擊button2個(gè)控件同時(shí)要有相應(yīng)舔亭,應(yīng)該怎么處理些膨?
8.點(diǎn)擊事件被攔截,但是相傳到下面的view钦铺,如何操作订雾?
9.實(shí)戰(zhàn)分析
10.請簡述Android事件傳遞機(jī)制, ACTION_CANCEL事件何時(shí)觸發(fā)矛洞?
進(jìn)入正題:
1.事件分發(fā)機(jī)制是怎么樣的洼哎?
事件分發(fā)機(jī)制是一種責(zé)任鏈模式
事件分發(fā)機(jī)制分為2種:View事件的分發(fā)和ViewGroup事件分發(fā)機(jī)制
先看簡單的View事件分發(fā)機(jī)制,demo如下
//子控件的ontouch方法影響子控件的函數(shù)
//onTouch====onTouchEvent====onClick;
/**
* 檢驗(yàn)view的事件分發(fā)順序沼本,點(diǎn)擊---dispatch-? Ontouch返回值為ture? 不執(zhí)行---ontouchEvent---onclick
*/
button1.setOnTouchListener(new View.OnTouchListener() {
? ? @Override
? ? public boolean onTouch(View v, MotionEvent event) {
? ? ? ? Log.d("TAG", "button1? on touch"+event.getAction());
? ? ? ? return true;
? ? }
});
/**
* 檢驗(yàn)view的事件分發(fā)順序噩峦, 點(diǎn)擊---dispatch-? Ontouch返回值為false執(zhí)行---ontouchEvent---onclick
*/
button2.setOnTouchListener(new View.OnTouchListener() {
? ? @Override
? ? public boolean onTouch(View v, MotionEvent event) {
? ? ? ? Log.d("TAG", "button1? on touch" + event.getAction());
? ? ? ? return false;
? ? }
});
demo地址:
源碼分析:然后我們來看一下View中dispatchTouchEvent方法的源碼:
public boolean dispatchTouchEvent(MotionEvent event) {
? ? if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
? ? ? ? ? ? mOnTouchListener.onTouch(this, event)) {
? ? ? ? return true;
? ? }
? ? return onTouchEvent(event);
}
整個(gè)View的事件轉(zhuǎn)發(fā)流程是:(原理是dispatchTouchEvent)
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中會進(jìn)行OnTouchListener的判斷,如果OnTouchListener不為null且返回true抽兆,則表示事件被消費(fèi)识补,onTouchEvent不會被執(zhí)行;否則執(zhí)行onTouchEvent辫红。
onClick方法是在onTouchEvent方法里調(diào)用的
總結(jié)
1.看判斷條件凭涂。如果沒有mOnTouchListener ,ontouch不執(zhí)行,onTouchEvent執(zhí)行
2.如果有mOnTouchListener,并且onTouchEvent=true,onTouchEvent不執(zhí)行
3.如果有mOnTouchListener,并且onTouchEvent=false,onTouchEvent執(zhí)行
ViewGruop的事件分發(fā):
多了一個(gè)攔截事件的方法:onInterceptTouchEvent
ViewGroup的dispathcTouchEvent方法:里面有onInterceptTouchEvent方法
onInterceptTouchEvent有兩個(gè)作用:1.攔截Down事件的分發(fā)贴妻。2.中止Up和Move事件向目標(biāo)View傳遞切油,使得目標(biāo)View所在的ViewGroup捕獲Up和Move事件。
總結(jié)ViewGroup發(fā)現(xiàn):dispathcTouchEvent開始-----disallownotIntercepter---onInterceptTouchEvent-----1.子類dispath() 2.父類TouchEvent方法
源碼:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier !=null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
? ? }
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
? ? if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
? ? }
boolean handled =false;
? ? if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
? ? ? ? final int actionMasked = action & MotionEvent.ACTION_MASK;
? ? ? ? // Handle an initial down.
? ? ? ? if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
? ? ? ? ? ? cancelAndClearTouchTargets(ev);
? ? ? ? ? ? resetTouchState();
? ? ? ? }
// Check for interception.
? ? ? ? final boolean intercepted;
? ? ? ? if (actionMasked == MotionEvent.ACTION_DOWN
? ? ? ? ? ? ? ? ||mFirstTouchTarget !=null) {
final boolean disallowIntercept = (mGroupFlags &FLAG_DISALLOW_INTERCEPT) !=0;
? ? ? ? ? ? if (!disallowIntercept) {
intercepted =onInterceptTouchEvent(ev);
/**默認(rèn)為false名惩,不攔截子控件的監(jiān)聽*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
? ? return false;
}
是否傳遞給子view澎胡,通過這個(gè)方法:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
? ? ? ? View child, int desiredPointerIdBits) {
final boolean handled;
? ? // Canceling motions is a special case.? We don't need to perform any transformations
// or filtering.? The important part is the action, not the contents.
? ? final int oldAction = event.getAction();
? ? if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
? ? ? ? if (child ==null) {
handled =super.dispatchTouchEvent(event);
? ? ? ? }else {
handled = child.dispatchTouchEvent(event);
? ? ? ? }
event.setAction(oldAction);
? ? ? ? return handled;
? ? }
實(shí)例分析:
當(dāng)一個(gè)Touch事件(觸摸事件為例)到達(dá)根節(jié)點(diǎn),即Acitivty的ViewGroup時(shí)娩鹉,它會依次下發(fā)滤馍,下發(fā)的過程是調(diào)用子View(ViewGroup)的dispatchTouchEvent方法實(shí)現(xiàn)的。
簡單來說底循,就是ViewGroup遍歷它包含著的子View巢株,調(diào)用每個(gè)View的dispatchTouchEvent方法,而當(dāng)子View為ViewGroup時(shí)熙涤,又會通過調(diào)用ViwGroup的dispatchTouchEvent方法繼續(xù)調(diào)用其內(nèi)部的View的dispatchTouchEvent方法阁苞。
上述例子中的消息下發(fā)順序是這樣的:①-②-⑤-⑥-⑦-③-④。
dispatchTouchEvent方法只負(fù)責(zé)事件的分發(fā)祠挫,它擁有boolean類型的返回值那槽,當(dāng)返回為true時(shí),順序下發(fā)會中斷等舔。
在上述例子中如果⑤的dispatchTouchEvent返回結(jié)果為true骚灸,那么⑥-⑦-③-④將都接收不到本次Touch事件
理論總結(jié):
1. Android事件分發(fā)是先傳遞到ViewGroup,再由ViewGroup傳遞到View的慌植。
Activity到----phoneWindow-----decorVIew----TitleBar------ViewGroup:是通過看源代碼發(fā)現(xiàn)的
2. 在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進(jìn)行攔截甚牲,onInterceptTouchEvent方法返回true代表不允許事件繼續(xù)向子View傳遞义郑,返回false代表不對事件進(jìn)行攔截,默認(rèn)返回false丈钙。
3.攔截的好好處在于調(diào)用誰的dispatchTouchEvent的方法非驮,誰出來點(diǎn)擊事件
4. 子View中如果將傳遞的事件消費(fèi)掉,ViewGroup中將無法接收到任何事件雏赦。
5.在ViewGroup中onInterceptTouchEvent方法若反回false,那么觸屏事件會繼續(xù)向下傳遞劫笙,
但如果沒有子View去處理這個(gè)事件,即子view的onTouchEvent沒有返回True
則最后還是由ViewGroup去處理這個(gè)事件星岗,也就又執(zhí)行了自己的onTouchEvent填大。
ViewGroup 類中,實(shí)際是沒有onTouchEvent 方法的俏橘,但是由于ViewGroup 繼承自View允华,
2.onTouch和onTouchevent和onClick的執(zhí)行順序?
View.dispatchEvent----ontouch-----ontouchEvent(方法down,up,判斷onclick時(shí)間)---onclick
ontouch先執(zhí)行,如果返回true敷矫,ontouchEvent,onClick都不執(zhí)行
ontouch先執(zhí)行例获,如果返回false,ontouchEvent,然后再是onclick方法
如果不重寫onTouchListerner方法曹仗。
onTouchEvent如果返回true,不會執(zhí)行onclick方法
onTouchEvent如果返回false,會執(zhí)行onclick方法
思考的問題:
1.View的dispatchTouchEvent重寫了會怎么樣榨汤,View的onTouchEvent重寫了會怎樣?
2.ViewGroup的dispatchTouchEvent重寫了會怎么樣怎茫,ViewGroup的onTouchEvent重寫了會怎樣收壕?
3.Button和ImageView有什么不一樣?
Button和ImageView效果不一樣:一個(gè)是自帶點(diǎn)擊,一個(gè)是要自己控制點(diǎn)擊
onTouch能夠得到執(zhí)行需要兩個(gè)前提條件轨蛤,第一mOnTouchListener的值不能為空蜜宪,第二當(dāng)前點(diǎn)擊的控件必須是enable的。因此如果你有一個(gè)控件是非enable的祥山,
那么給它注冊onTouch事件將永遠(yuǎn)得不到執(zhí)行圃验。對于這一類控件,如果我們想要監(jiān)聽它的touch事件缝呕,就必須通過在該控件中重寫onTouchEvent方法來實(shí)現(xiàn)澳窑。
/**ImageView默認(rèn)是不能點(diǎn)擊事件的,要想點(diǎn)擊的話必須手動設(shè)置*/
imageView.setClickable(true);
imageView.setOnClickListener(new View.OnClickListener() {
? ? @Override
? ? public void onClick(View v) {
? ? ? ? Log.e("TAG","imageView setOnTouchListener");
? ? }
});
5.如何理解消費(fèi)供常?
如果onTouch為true摊聋,代表消費(fèi)了。不會執(zhí)行onTouchevent了
如果子類的onTouchevent為true栈暇,代表消費(fèi)了麻裁,父類不會執(zhí)行onTouchevent
6.可以達(dá)到父控件和子空間同時(shí)點(diǎn)擊嗎?
如下:7問題
7.ListView上的button,點(diǎn)擊button2個(gè)控件同時(shí)要有相應(yīng)煎源,應(yīng)該怎么處理色迂?
分析:從listview分發(fā)到button
在listView的空白區(qū)域:執(zhí)行l(wèi)istview的onTouchEvent方法。攔截button
在button的點(diǎn)擊區(qū)域: 不攔截薪夕,消費(fèi)button的onTouchEvent事件脚草。
具體方案:在listView的OnInterceptTouchEvent()方法里面赫悄。判斷區(qū)域原献。是否攔截
順便提一下父控件和子控件狀態(tài)跟隨
當(dāng)父控件是布局而子控件是控件時(shí),如果要設(shè)置點(diǎn)擊效果埂淮,可以在父布局里面加上android:clickable="true" 姑隅,在子控件里面設(shè)置android:clickable="false",并設(shè)置狀態(tài)跟隨父布局android:duplicateParentState="true"倔撞,至于效果讲仰,則隨自己寫吧
8.點(diǎn)擊事件被攔截,但是相傳到下面的view痪蝇,如何操作鄙陡?
重寫子類的requestDisallowInterceptTouchEvent()方法返回true,就不會執(zhí)行父類的onInterceptTouchEvent()躏啰,即可將點(diǎn)擊事件傳到下面的View趁矾。
9.實(shí)戰(zhàn)分析
1.如何讓子類只有觸摸事件,沒有點(diǎn)擊事件
2.如何讓子類既有觸摸事件又有點(diǎn)擊事件
3.如何讓父類只有觸摸事件给僵,沒有點(diǎn)擊事件
4.如何讓父類既有觸摸事件又有點(diǎn)擊事件
1.onTouch為true毫捣,事件被消費(fèi)了,ontouchevent不執(zhí)行帝际,點(diǎn)擊也就不會執(zhí)行
button.setOnTouchListener(new View.OnTouchListener() {
@Override
? ? public boolean onTouch(View v, MotionEvent event) {
Log.d("peng"," button.setOnTouchListener"+event.getAction());
return true;
? ? }
});
button.setOnClickListener(new View.OnClickListener() {
@Override
? ? public void onClick(View v) {
Log.d("peng"," button.setOnClickListener");
? ? }
});
2.onTouch為false,ontouchevent會執(zhí)行蔓同,這樣,點(diǎn)擊事件會執(zhí)行
button.setOnTouchListener(new View.OnTouchListener() {
@Override
? ? public boolean onTouch(View v, MotionEvent event) {
Log.d("peng"," button.setOnTouchListener"+event.getAction());
return false;
? ? }
});
button.setOnClickListener(new View.OnClickListener() {
@Override
? ? public void onClick(View v) {
Log.d("peng"," button.setOnClickListener");
? ? }
});
默認(rèn)情況下蹲诀,子view的
onTouchEvent返回true斑粱,消費(fèi)
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean pass=super.onTouchEvent(event);
? ? Log.d("peng","onTouchEvent onTouchEvent"+pass);
? ? return pass;
}
3.父類想要響應(yīng),子類不能消費(fèi)脯爪,onTouch false ,onTouchEvent 也為false,onTouch 為false
button.setOnClickListener(new View.OnClickListener() {
@Override
? ? public void onClick(View v) {
Log.d("peng"," button.setOnClickListener");
? ? }
});
viewHead.setOnTouchListener(new View.OnTouchListener() {
@Override
? ? public boolean onTouch(View v, MotionEvent event) {
Log.d("peng"," viewHead.setOnTouchListener"+event.getAction());
return true;
? ? }
});
viewHead.setOnClickListener(new View.OnClickListener() {
@Override
? ? public void onClick(View v) {
Log.d("peng"," viewHead.setOnClickListener");
? ? }
});
public class Myviewextends android.support.v7.widget.AppCompatButton {
public Myview(Context context) {
super(context);
? ? }
public Myview(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
? ? }
public Myview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
? ? }
@Override
? ? public boolean onTouchEvent(MotionEvent event) {
Log.d("peng","Myview onTouchEvent onTouchEvent"+false);
return false;
? ? }
4.
父類想要響應(yīng)则北,子類不能消費(fèi),onTouch false ,onTouchEvent 也為false,onTouch 為false .父類的onTouchEvent也要重寫true披粟,代表消費(fèi)了咒锻。
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("peng","MyParentView onTouchEvent");
return true;
}
1.如何讓子類只有觸摸事件,沒有點(diǎn)擊事件
2.如何讓子類既有觸摸事件又有點(diǎn)擊事件
3.1如何讓父類只有觸摸事件守屉,沒有點(diǎn)擊事件,.如何讓父類既有觸摸事件又有點(diǎn)擊事件
子控件拿到事件之后惑艇,先判斷是否設(shè)置了OnTouchListener, 如果設(shè)置了,則調(diào)用OnTouchListener的onTouch方法,如果返回true滨巴,事件已經(jīng)處理到此結(jié)束思灌,則跳過onTouchEvent方法,否則調(diào)用onTouchEvent方法恭取,當(dāng)onTouchEvent方法返回true泰偿,則事件處理到此結(jié)束,上面的父控件就不會再調(diào)用onTouchEvent方法
實(shí)戰(zhàn)分析二:我想讓webview在應(yīng)用里面后臺運(yùn)行?看不到界面
2個(gè)viewGoup
1個(gè)webview和一個(gè)ViewGroup(包含4個(gè)btn)
點(diǎn)擊btn的時(shí)候蜈垮,會消費(fèi)掉事件
問題:viewgroup點(diǎn)擊空白也是會有事件的耗跛,如果給他添加監(jiān)聽
加入點(diǎn)擊空白頁面,Viewgroup的子View消費(fèi)掉攒发。(webview的子空間)
問題:不想讓子控件消費(fèi)怎么做
1.可以自己消費(fèi)掉调塌,ontouch==true。不再傳遞了惠猿,onclick事件就不會執(zhí)行
2.可以讓另外一個(gè)viewgourp自己消費(fèi)掉羔砾。
ontouch==true,自己的onclick不會在執(zhí)行偶妖,但是這個(gè)子view還是響應(yīng)了
原因:因?yàn)闆]有攔截姜凄,走了子類的dispathevent方法。子類的dispathevent----ontouch---子類的onclick方法消費(fèi)了趾访。
所以消費(fèi)是onclick和ontouch方法态秧,但是onclick方法也是要先調(diào)用ontouch---ontouchevnet----onclick。
總結(jié):最后的消費(fèi)???的時(shí)onTouch事件
實(shí)戰(zhàn)分析三:
1.有一個(gè)布局腹缩,然后有一個(gè)textview屿聋,然后想點(diǎn)擊整個(gè)布局有點(diǎn)擊事件
結(jié)果發(fā)現(xiàn):點(diǎn)擊textview的區(qū)域沒有響應(yīng),因?yàn)閠extview把焦點(diǎn)占用了藏鹊。
為了讓整個(gè)區(qū)域都有效果的話润讥,把textview設(shè)置成
? ??android:clickable="false"
? ? android:id="@+id/tv_sport_data_second_value"
? ? android:layout_width="wrap_content"
? ? android:layout_height="wrap_content"
? ? android:textSize="@dimen/dp_20"
? ? android:textColor="@color/color_333333"
? ? android:textStyle="bold"
? ? android:clickable="false"
? ? android:text="0"
? ? android:layout_below="@id/ll_second_title"
? ? android:layout_marginTop="@dimen/dp_6"
? ? android:id="@+id/rl_sport_data_second"
? ? android:layout_width="0dp"
? ? android:layout_height="wrap_content"
? ? android:layout_weight="1"
? ? >
? ? ? ? android:id="@+id/ll_second_title"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:orientation="horizontal"
? ? ? ? android:gravity="center_vertical"
? ? ? ? >
? ? ? ? ? ? android:id="@+id/tv_sport_data_second_title"
? ? ? ? ? ? android:layout_width="wrap_content"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:textColor="@color/color_333333"
? ? ? ? ? ? android:textSize="@dimen/dp_14"
? ? ? ? ? ? android:text="@string/string_sport_rank_total_sport"
? ? ? ? ? ? ></TextView>
10.請簡述Android事件傳遞機(jī)制, ACTION_CANCEL事件何時(shí)觸發(fā)盘寡??
https://blog.csdn.net/kingofhacker/article/details/75111372
ACTION_CANCELL:手指保持按下操作楚殿,并從當(dāng)前控件轉(zhuǎn)移到外層控件時(shí)觸發(fā)
關(guān)于第一個(gè)問題,不做任何解釋竿痰。 (悅動圈里面的滑動開關(guān))
關(guān)于ACTION_CANCEL何時(shí)被觸發(fā)脆粥,系統(tǒng)文檔有這么一種使用場景:在設(shè)計(jì)設(shè)置頁面的滑動開關(guān)時(shí),如果不監(jiān)聽ACTION_CANCEL影涉,在滑動到中間時(shí)变隔,如果你手指上下移動,就是移動到開關(guān)控件之外蟹倾,則此時(shí)會觸發(fā)ACTION_CANCEL匣缘,而不是ACTION_UP猖闪,造成開關(guān)的按鈕停頓在中間位置。
意思是當(dāng)滑動的時(shí)候就會觸發(fā)肌厨,不知道大家搞沒搞過微信的長按錄音培慌,有一種狀態(tài)是“松開手指,取消發(fā)送”柑爸,這時(shí)候就會觸發(fā)ACTION_CANCEL吵护。
https://blog.csdn.net/cufelsd/article/details/89471402
https://blog.csdn.net/epubit17/article/details/80342004
參考博客:
http://blog.csdn.net/guolin_blog/article/details/9097463