大廠Android端可視化埋點的實現(xiàn)

Android端可視化埋點的實現(xiàn)

***導(dǎo)語 ***客戶端埋點是數(shù)據(jù)收集的最基本手段奖慌,對于一款A(yù)PP來說星瘾,代碼埋點(就是在業(yè)務(wù)代碼中组力,在需要埋點的view的點擊事件回調(diào)處做點擊上報的處理瘟斜,當(dāng)此view被點擊時县貌,進(jìn)行相應(yīng)事件的上報)是最為常見的埋點方式术陶,但由于業(yè)務(wù)迭代速度很快,手動埋點方案雖然靈活多變煤痕,但是極大的增加了客戶端開發(fā)人員的工作量梧宫。開發(fā)完成業(yè)務(wù)功能需要花費很大的精力處理埋點事宜接谨,而且隨著迭代版本,新的需求增加塘匣,埋點的數(shù)量會越來越多脓豪,這些老舊埋點的維護(hù)工作也需要付出不小的努力。并且忌卤,代碼埋點需要跟版本迭代扫夜,如果在開發(fā)過程中忘記埋點,就只能等待下個版本再埋點驰徊。所以笤闯,如果能夠?qū)崿F(xiàn)不需要或者很少需要開發(fā)人員介入就能實現(xiàn)根據(jù)不同業(yè)務(wù)場景埋點的功能對于提高版本迭代速度和開發(fā)人員的幸福感絕對是一件非常有價值的事情。 所以棍厂,為了減少代碼埋點的不便颗味,不需要開發(fā)人員介入,使運營或者用研的同學(xué)就可以隨時動態(tài)調(diào)整需要上報的點牺弹,我們需要改變下當(dāng)前的埋點方式浦马。

一、需求背景

1张漂、什么是埋點

預(yù)先在目標(biāo)應(yīng)用采集數(shù)據(jù)晶默,對特定用戶行為或事件進(jìn)行捕獲、處理以一定方式上報至服務(wù)器的相關(guān)技術(shù)及其實施過程鹃锈。

在大數(shù)據(jù)話的今天荤胁,一個好的產(chǎn)品就應(yīng)該符合用戶的需求,我們做統(tǒng)計就是收集用戶的行為屎债,分析用戶的喜好仅政,不斷的根據(jù)用戶行為改進(jìn)我們的產(chǎn)品,當(dāng)然專業(yè)的需要數(shù)據(jù)分析來完成盆驹,我們需要的就是為分析提供用戶數(shù)據(jù)圆丹。

原來我們的方式是代碼埋點,在每一個要上報的view的點擊事件都都要由開發(fā)人員加入上報的代碼躯喇。

public void onClick(View v) {
    ..
    ..
    //在點擊事件回調(diào)中走統(tǒng)一的上報
    ClickReport.collect(ClickReport.MineAction.MINE_MORE_PRIVILEGE);
    ..
    ..
}
復(fù)制代碼

2辫封、存在的問題

但由于業(yè)務(wù)迭代速度很快,手動埋點方案雖然靈活多變廉丽,但是也存在很多問題:

1倦微、開發(fā)完成業(yè)務(wù)功能需要花費很大的精力處理埋點事宜,而且多余的代碼顯得很冗余正压,浪費開發(fā)時間

2欣福、隨著迭代版本,新的需求增加焦履,埋點的數(shù)量會越來越多拓劝,這些老舊埋點的維護(hù)工作也需要付出不小的努力雏逾。

3、代碼埋點需要跟版本迭代郑临,無法實現(xiàn)動態(tài)化的配置栖博,如果在開發(fā)過程中忘記埋點,就只能等待下個版本再埋點厢洞。

所以仇让,為了減少代碼埋點的不便,減少開發(fā)人員介入犀变,使運營或者用研的同學(xué)就可以隨時動態(tài)化的調(diào)整需要上報的點妹孙,我們需要改變下當(dāng)前的埋點方式,開發(fā)一個合適客戶端埋點的上報模式获枝。

二蠢正、可視化埋點

1、埋點的三種方式:

因為不同的app也會有自己的異化處理省店,埋點的方式也是根據(jù)特殊情況有這眾多方案的嚣崭,但是大體上現(xiàn)在主要流行的就是三種方案:

1、代碼埋點:將收集數(shù)據(jù)的代碼直接寫在需要的地方懦傍,當(dāng)用戶點擊某個控件或者打開某個頁面時調(diào)用到該部分代碼完成數(shù)據(jù)的收集雹舀。

  • 優(yōu)點:準(zhǔn)確性高,收集數(shù)據(jù)和發(fā)送數(shù)據(jù)都能精確控制粗俱,同時能方便的設(shè)置自定義屬性说榆,自定義控件,自定義View等寸认。
  • 缺點:埋點工作量大签财,更新代價大,需要跟版本偏塞,不靈活唱蒸。

2、可視化埋點:根據(jù)可視化界面進(jìn)行配置然后上報后臺灸叼,后臺向終端下發(fā)配置文件神汹,終端點擊時獲取當(dāng)前點擊的控件根據(jù)配置文件進(jìn)行選擇上報。

  • 優(yōu)點:數(shù)據(jù)量相對精確古今,覆蓋范圍大屁魏。
  • 缺點:可視化平臺搭建困難,控件樹的元素的精確識別較為麻煩捉腥。

3氓拼、無埋點:與可視化埋點基本一致。不同點在于可視化埋點是根據(jù)配置文件收集數(shù)據(jù),無埋點是預(yù)先收集所有的用戶行為披诗,然后根據(jù)配置文件來提取數(shù)據(jù)。

  • 優(yōu)點:數(shù)據(jù)覆蓋全立磁,開發(fā)人員工作量小呈队。
  • 缺點:數(shù)據(jù)量大,后端篩選分析工作量大唱歧,每個點都統(tǒng)計對性能有隱患宪摧。

2、為什么選擇可視化的埋點

對于無埋點颅崩,和可視化埋點的方案很相似几于,區(qū)別在于無埋點需要將所以的點擊都收集,后續(xù)在根據(jù)配置文件在分析想要的數(shù)據(jù)沿后,這種方式對于客戶端來說很省事沿彭,客戶端也不用分析是哪個點,只要用戶點擊了就上報尖滚,但是對于后端的篩選和分析一定的影響喉刘,而且可能我們需要統(tǒng)計的點并不是很多時可能大多數(shù)上報的點都不是我們想要的,這時用無埋點的方式感覺有些得不償失漆弄。

MTA也有一個可視化埋點的功能睦裳,但是咱們我們要上報的平臺有很多,比如要上報到羅盤或者我們自己服務(wù)器的統(tǒng)計平臺撼唾,僅僅使用MTA的sdk無法滿足各種平臺的上報廉邑,所以我們選擇可視化埋點的方式統(tǒng)計用戶的行為,并且在統(tǒng)一的模塊進(jìn)行各個平臺的上報倒谷。

三蛛蒙、可視化埋點方案探討與具體實現(xiàn)

1、方案介紹

主要的流程可以分為配置埋點模式恨锚,和用戶模式宇驾。

上報的信息:

{
    String ViewId;   //當(dāng)前View的唯一ID 由路徑獲得
    String EventId;  //View的事件ID 由產(chǎn)品相關(guān)人員配置
}
復(fù)制代碼

在配置模式中,由產(chǎn)品同學(xué)或者運營同學(xué)配置需要上報的點擊view猴伶,將點擊view的唯一標(biāo)識viewID和事先定好的用于分析的eventID綁定形成一個配置map表课舍,配置結(jié)束后,將已經(jīng)配置完成的map表上傳至后臺他挎,完成配置模式筝尾。

[圖片上傳失敗...(image-4aaf40-1649852259824)]

<figcaption style="display: block;">圖1:可視化埋點流程</figcaption>

在配置模式中,由產(chǎn)品同學(xué)或者運營同學(xué)配置需要上報的點擊view办桨,將點擊view的唯一標(biāo)識viewID和事先定好的用于分析的eventID綁定形成一個配置map表筹淫,配置結(jié)束后,將已經(jīng)配置完成的map表上傳至后臺呢撞,完成配置模式损姜。

在用戶模式下饰剥,就是當(dāng)用戶開啟app時,會拉取到在產(chǎn)品或者運營同學(xué)在配置模式下配置的map表摧阅,然后當(dāng)用戶點擊相應(yīng)點時汰蓉,可以通過某種方式與配置表中的點做對照,如果在配置表中棒卷,就走上報邏輯顾孽。

通過上述流程,可以總結(jié)出以下三個問題:

  • 如何獲取當(dāng)前點擊的view比规;
  • 如何確定view的唯一標(biāo)識viewID若厚;
  • 用戶點擊view的上報流程;

下面針對這三個問題進(jìn)行分析和方案的確定蜒什。

2测秸、獲取當(dāng)前點擊的view

因為我門需要在一個統(tǒng)一的地方獲取當(dāng)前的點擊事件,來做統(tǒng)一的配置或者篩選上報工作灾常,這些工作不可能在每一個view的點擊事件回調(diào)中完成乞封,比較普遍的做法就是遍歷當(dāng)前activity下的viewtree,根據(jù)UI布局的特性和Android點擊事件傳遞機(jī)制來找到當(dāng)前的view岗憋。

主流的方案:通過位置遍歷計算

讓項目中主框架的BaseFragmentActivity基類重寫Activity的dispatchTouchEvent方法肃晚,當(dāng)touch button時,可以獲取到按下(DOWN)和抬起(UP)時產(chǎn)生的MotionEvent對象仔戈。這個MotionEvent對象有兩個方法关串,getRawX()和getRawY(),通過這兩個方法我們可以獲取到“點擊位置”在界面中的坐標(biāo)监徘。通過rootview可以層層遍歷其下的子view以及所有子View上的控件晋修,這些View和控件在屏幕中的坐標(biāo)和寬高我們是可以獲取到的。然后搜索所有的子View或者控件的布局區(qū)域是否包含“點擊位置”凰盔,從而來判斷哪個View或控件被點擊墓卦。

/**
 * 通過遍歷的方式獲取當(dāng)前view
 */
private View searchClickView(View view, MotionEvent event,StringBuffer stringBuffer) {
    ..
    ..
    if (isInView(view, event) && view.getVisibility() == View.VISIBLE) {  //這里一定要判斷View是可見的
        if (view instanceof ViewGroup) {    //遇到一些Layout之類的ViewGroup,繼續(xù)遍歷它下面的子View
            ViewGroup group = (ViewGroup) view;
            for (int i = group.getChildCount() - 1; i >= 0; i--) {
                View chilView = group.getChildAt(i);
                clickView = searchClickView(chilView, event,stringBuffer);
                if (clickView != null) return clickView;
            }
        }
    }
    ..
    ..
}
復(fù)制代碼

但是這種獲取view的方式畢竟是靠遍歷來獲得的户敬,難免對性能有一些影響落剪,其實通過viewGroup的TouchTarget的類型可以有一種更好的方式去得到當(dāng)前的view。

我們的方案:通過TouchTarget類型獲取

ViewGroup中有一個TouchTarget 類型的變量 mFirstTouchTarget尿庐,表示消費當(dāng)前觸摸事件的控件列表忠怖。例如,點擊屏幕上一個按鈕抄瑟,那么按鈕所在ViewGroup的mFirstTouchTarget 變量就指向這個按鈕凡泣。當(dāng)ViewGroup派發(fā)觸摸事件時,他會首先判斷變量mFirstTouchTarget是否存在,如果變量存在鞋拟,會循環(huán)遍歷TouchTarget鏈表元素骂维,找到能處理該事件的View并將MotionEvent 派發(fā)給該View。如果不存在TouchTarget贺纲,ViewGroup 會循環(huán)遍歷所有child view席舍,直到找到一個能處理該事件的View,并將該View作為first touch target 賦值給mFirstTouchTarget哮笆。 當(dāng)用戶觸發(fā)Down事件時,會執(zhí)行如下邏輯汰扭,尋找消費當(dāng)前事件的TouchTarget稠肘。

if (actionMasked == MotionEvent.ACTION_DOWN){
    //如果是down事件,遍歷child萝毛,找到TouchTarget
    ..
    ..
    final View[] children = mChildren;
       ..
       ..
       if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
          // child 消費了觸摸事件
          ..
          ..
          // 根據(jù)消費了觸摸事件的View創(chuàng)建TouchTarget
           newTouchTarget = addTouchTarget(child, idBitsToAssign);
          ..
          ..
          break;
      }
}
復(fù)制代碼

addTouchTarget就是獲取子view的touchTarget项阴,并將其加入到TouchTarget鏈的最頂部,因為是從獲取到DOWN事件的view層層向上遞歸笆包,所以TouchTarget鏈的尾端就是目標(biāo)view环揽。

private TouchTarget addTouchTarget(View child, int pointerIdBits) {

    TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
    target.next = mFirstTouchTarget;
    mFirstTouchTarget = target;

    return target;
}
復(fù)制代碼

當(dāng)觸發(fā)Down事件并且找到TouchTarget,或者觸發(fā)非Down事件時庵佣,執(zhí)行如下處理邏輯歉胶。

if (mFirstTouchTarget == null) {
    // No touch targets so treat this as an ordinary view.
    handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
} else {
    //Down事件發(fā)生時找到TouchTarget,或者非Down事件直接執(zhí)行如下邏輯
    // 將事件派發(fā)給TouchTarget表示的View
    TouchTarget predecessor = null;
    TouchTarget target = mFirstTouchTarget;
    while (target != null) {
        final TouchTarget next = target.next;
        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
            handled = true;
        } else {
            final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;
            if (dispatchTransformedTouchEvent(ev, cancelChild,target.child,target.pointerIdBits)) {
               //指定TouchTarget對應(yīng)的View正確消費了事件
                handled = true;
             }
             ..
             ..
         }
     ..
     ..
     }
}
復(fù)制代碼

因為根據(jù)view的點擊事件可知巴粪,view點擊時事件是從根節(jié)點開始向下進(jìn)行傳遞通今,如果viewgroup存在TouchTarget,會從TouchTarget的成員變量中獲取當(dāng)前的處理事件的view或者viewgroup肛根,如果該viewgroup存在TouchTarget辫塌,就繼續(xù)向下查找,直到當(dāng)前的viewgroup的TouchTarget為空時派哲,就說明是此viewgroup消費了這個事件臼氨;或者直到傳遞到一個view而不是viewgroup時,此view就是當(dāng)前用戶點擊的view芭届。

因為mFirstTouchTarget是一個TouchTarget類型的私有成員變量储矩,我們需要通過反射的方式去獲取:

/**
 *  獲取當(dāng)前點擊的view
 */
public final View getTouchTarget(final ViewGroup vg) {
    ..
    ..
    while (currentTarget != null) {
        Field fieldTouchTarget = ReflectHelper.getDeclaredField(ViewGroup.class, "mFirstTouchTarget");
        Object touchTarget =  ReflectHelper.getFieldValue(fieldTouchTarget, currentTarget);
        if (touchTarget == null) {
             break;
        }
        Field fieldChild = ReflectHelper.getDeclaredField(touchTarget.getClass(), "child");
        View child = (View) ReflectHelper.getFieldValue(fieldChild, touchTarget);
        if (child instanceof ViewGroup) {
            preTarget = currentTarget;
            currentTarget = child;
            continue;
        } else if (child instanceof View) {
            currentTarget = child;
            break;
        }
    }
    ..
    ..
return currentTarget;
}
復(fù)制代碼

利用ViewGroup的這種事件處理機(jī)制褂乍,可以在activity的dispatchTouchEvent中添加處理邏輯椰苟,如果接收到down事件,就讓其傳遞下去使mFirstTouchTarget被賦值树叽,當(dāng)接收到up事件時舆蝴,做相應(yīng)的獲取當(dāng)前view邏輯,通過rootview的mFirstTouchTarget獲取其子view,如果子view的mFirstTouchTarget不為空洁仗,就通過這種TouchTarget的鏈?zhǔn)疥P(guān)系獲得這次點擊行為的最終view层皱。

3、唯一的標(biāo)識一個view

根據(jù)上述流程赠潦,用戶模式下叫胖,當(dāng)用戶點擊某個view時,需要一個唯一的viewID用來在配置表中進(jìn)行查找她奥,并且需要與其他的view做區(qū)分瓮增;在配置模式下也需要通過唯一標(biāo)示的viewID作為key與作為value的eventID共同上報到配置表中,那么應(yīng)該如何選取這個唯一的標(biāo)示呢哩俭?

主流的方案:遍歷viewTree生成路徑

對于view來說绷跑,Android系統(tǒng)提供了一個ID,view.getId()即可獲得一個int型的id用于區(qū)分View,但是這個ID因為以下兩個原因卻并不能滿足我們的需要凡资。

  • 有相當(dāng)一部分view是NO_ID砸捏,比如在布局文件中未指定id,或者直接在代碼里面new出來view,view.getId()返回的全部都是NO_ID
  • 這個ID是不穩(wěn)定的隙赁,由于這個ID其實就是每次編譯產(chǎn)生的R文件中的int常量垦藏,因此同一個按鈕,兩個版本編譯出來的ID很可能是不一樣的伞访。

所以為了使viewID不受版本和手機(jī)的影響掂骏,通常的方式就是利用所屬activity+viewtree的路徑形式來構(gòu)建viewID。

[圖片上傳失敗...(image-23560b-1649852259824)]

<figcaption style="display: block;">圖2:view層級示意圖</figcaption>

通過遍歷當(dāng)前activity的viewTree的方式厚掷,得到當(dāng)前view的父類芭挽、祖父類一直到根view,然后根據(jù)view的路徑層級關(guān)系再來確定viewID蝗肪。但是這又需要遍歷袜爪,我們不應(yīng)該每獲得一個view都要進(jìn)行遍歷,這樣也是比較繁瑣的薛闪。

我們的方案:通過TouchTarget鏈獲取

在第一個問題如果獲取當(dāng)前view時已經(jīng)說過辛馆,可以通過viewGroup的TouchTarget鏈來獲取當(dāng)前的點擊view,并且鏈上的層級關(guān)系就是從根view到父view再到子view的一個層級豁延,所以在獲取view的同時就可以順便記錄層級關(guān)系昙篙,來作為viewID。

可以在獲取當(dāng)前view的過程中诱咏,記錄每一個節(jié)點TouchTarget的mFirstTouchTarget苔可,這樣就可以在得到點擊view的同時,也得到相應(yīng)的view的路徑比如:DecorView-LinearLayout-FrameLayout-RelativeLayout-Button袋狞,再配置上view的class類名等信息焚辅,我們就可以同時確定目標(biāo)控件的唯一標(biāo)識映屋,將viewID通過setTag進(jìn)行設(shè)置,在需要的地方取出同蜻。所以在方案上我們還是用利用獲取viewgroup的TouchTarget來得到當(dāng)前點擊的view棚点,同時也可以根據(jù)這種鏈?zhǔn)疥P(guān)系來記錄點擊view的從父類到子view的路徑,從而將此路徑作為當(dāng)前view的唯一標(biāo)示viewID湾蔓。

4瘫析、用戶端的埋點上報流程

對于可視化埋點操作來說,在用戶模式下默责,當(dāng)用戶開啟app時進(jìn)行配置表拉取的操作贬循,然后當(dāng)用戶進(jìn)行點擊時,通過遍歷當(dāng)前activity下的viewtree獲得點擊的view桃序,或者通過上述分析的查找viewtree根視圖的TouchTarget鏈表的方式來獲取當(dāng)前的view杖虾,然后從配置表中查找出與點擊view的viewID一致的數(shù)據(jù),進(jìn)行上報葡缰。

主流的方案:view代理監(jiān)聽的方式

還有一種比較常用可視化埋點方式就是view的代理監(jiān)聽的方式,ActivityLifecycleCallbacks忱反,用來監(jiān)聽Activity生命周期泛释,當(dāng)activity被開啟時,遍歷當(dāng)前activity下的所有view温算,如果view在配置表中怜校,就設(shè)置當(dāng)前view的setAccessibilityDelegate。

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class VisualAnalysisManager extends View.AccessibilityDelegate{
     ..
     ..
     view.setAccessibilityDelegate(this);
     ..
     ..
}
復(fù)制代碼

當(dāng)view產(chǎn)生了click或者long_click等點擊事件時注竿,分析View的源碼在處理點擊事件的回調(diào)時調(diào)用了 View.performClick 方法茄茁,內(nèi)部調(diào)用了sendAccessibilityEvent,會在響應(yīng)原有的Listener方法后巩割,發(fā)送消息給AccessibilityDelegate裙顽,然后在繼承AccessibilityDelegate的類中重寫sendAccessibilityEvent方法來上報自動埋點事件。

public boolean performClick() {
    .. 
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    ..
}
public void sendAccessibilityEvent(int eventType) {
    if (mAccessibilityDelegate != null) {
        mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
    } else {
        sendAccessibilityEventInternal(eventType);
    }
}
復(fù)制代碼

代理監(jiān)聽的方式就是在ActivityLifecycleCallbacks監(jiān)聽到activity開啟時對viewtree進(jìn)行遍歷宣谈,如果遍歷view的viewID在后臺下發(fā)的配置表中愈犹,就設(shè)置setAccessibilityDelegate,這種方式比較常用闻丑,但也要求sdk版本大于14漩怎。

我們的方案:獲取viewID與配置表對比

用戶模式與配置模式都需要獲取當(dāng)前點擊的view,所以除類用上述view的代理監(jiān)聽的方式進(jìn)行上報嗦嗡,還可以與配置模式相同勋锤,用TouchTarget鏈表的方式來獲取當(dāng)前的view,再判斷當(dāng)前的點擊view是否需要上報侥祭。本需求在用戶模式下也是采用TouchTarget鏈表的方式獲取當(dāng)前view進(jìn)行上報叁执,與配置模式統(tǒng)一茄厘,方便開發(fā)也避免代理監(jiān)聽的方式在在sdk小于14無法使用的困擾。

在獲取view中徒恋,需要獲取當(dāng)前的activity蚕断,以及我們可能需要在當(dāng)前activity生命周期中去做一些設(shè)置以及初始化等操作,最簡單的方式是通過ActivityLifecycleCallbacks來監(jiān)聽activity的生命周期入挣。然后在回調(diào)中做相應(yīng)的操作亿乳,可以實現(xiàn)代碼解藕。

public class ActivityTrackUtils implements ActivityTracer.ActivityLifecycleCallbacks {
    ..
    ..
    private ActivityTracer activityTracer;
public void start(Application app) {
activityTracer.install(app);
        activityTracer.registerActivityLifecycleCallbacks(this);
    }
@Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (callback != null) {
callback.onActivityCreated(activity, savedInstanceState);
        }
    ..
    ..
復(fù)制代碼

四径筏、實現(xiàn)中遇到的問題以及解決方式

1葛假、兼容性問題上的優(yōu)化

上面第四章也說明了,獲取當(dāng)前view是通過反射的方式獲取viewGroup中的私有成員滋恬,這樣就可能因為不同手機(jī)api版本不同聊训,源碼會有一些差異,可能在某個版本的api中就無法獲得恢氯,雖然這樣的概率很小带斑,但是為了保證代碼的穩(wěn)定,提高魯棒性勋拟,還是可以提供一個降級方案:

public final View getTouchTarget(final ViewGroup vg,final MotionEvent event) {
    ..
    ..
    //方案A
    targetView = mVisualAnalysisHelper.getTouchTarget(vg);
    //方案B
    //如果mFirstTouchTarget的方式查詢不出來勋磕,走降級方案
    if(targetView == null){
        targetView = mVisualAnalysisHelper.getTouchTargetForPlanB(vg,event);
    }
    ..
復(fù)制代碼

可以將一個問題中的獲取view的主流方法作為降級方案,通過遍歷當(dāng)前viewTree的形式敢靡,判斷當(dāng)前的坐標(biāo)是否在view的范圍之中挂滓,并循環(huán)遍歷viewGroup中的子view,最終可以獲取到當(dāng)前的點擊view啸胧。

2赶站、特殊控件的處理

有些控件可能會提前截取點擊事件單獨做處理,這樣事件無法下發(fā)纺念,導(dǎo)致無法獲取當(dāng)前的view贝椿,TouchTarget為null,比如微云項目中有個大標(biāo)題中的控件陷谱,在我查找了眾多原因后發(fā)現(xiàn)其實就是它在onInterceptTouchEvent 方法中自己做了處理团秽,阻止了事件的傳遞,對于這種特殊的問題叭首,只能做一下單獨的處理习勤,在它處理之前,判斷它的子view中是否有我們需要的view焙格,如果需要就獲取view做相應(yīng)的處理图毕。

還有一個問題就是dialog中的view是無法處理的,因為在Android中眷唉,dialog其實與activity都有自己的wondow予颤,獨立與activity的view層級囤官,它與activity有各自的點擊事件傳遞,所以要針對dialog的事件傳遞做單獨的處理蛤虐,在baseDialog的dispatchTouchEvent中做與activity相同的判斷党饮,根據(jù)模式的不同做不同的處理。

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ..
        ..
        /**
         * 可視化埋點 在配置模式下就返回false驳庭,非配置模式下判斷點是否在上報map中刑顺,有就上報
         */
        VisualAnalysisManager visualAnalysisManager = VisualAnalysisManager.getInstance();
        if(ev.getAction() == MotionEvent.ACTION_UP)
if (visualAnalysisManager.getIsInConfigVisual()) {
            //配置模式
            visualAnalysisManager.handleTouchEventInConfigMode(ev);
            return false;
        }else {
            //用戶模式
//截取up事件,讓down事件傳遞下去,來獲取當(dāng)前點擊的view
            visualAnalysisManager.handleTouchEventInNormalMode(ev);
        }
        ..
        ..
復(fù)制代碼

五饲常、待優(yōu)化的viewID問題

viewID是根據(jù)view的層級來確定的蹲堂,如果項目進(jìn)行重構(gòu)或者變更層級,相同view的viewID就會變化贝淤,導(dǎo)致很多view要重新配置柒竞,有一種約束ID的方案,就是單獨生成一個view與viewID的對應(yīng)表播聪,但是這樣在添加新view時又要做相應(yīng)的對應(yīng)朽基,也會帶來開發(fā)上的不便利,所以目前還是維持現(xiàn)有的生成viewID的方案离陶,當(dāng)遇到重構(gòu)或者層級變動的問題時就只能將上報的點遷移并重新生成viewID再上報稼虎,這是一個待優(yōu)化的方向,后續(xù)想到合適的方案時會將其優(yōu)化枕磁。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渡蜻,一起剝皮案震驚了整個濱河市术吝,隨后出現(xiàn)的幾起案子计济,更是在濱河造成了極大的恐慌,老刑警劉巖排苍,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沦寂,死亡現(xiàn)場離奇詭異,居然都是意外死亡淘衙,警方通過查閱死者的電腦和手機(jī)传藏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彤守,“玉大人毯侦,你說我怎么就攤上這事【叩妫” “怎么了侈离?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長筝蚕。 經(jīng)常有香客問我卦碾,道長铺坞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任洲胖,我火速辦了婚禮济榨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绿映。我一直安慰自己擒滑,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布绘梦。 她就那樣靜靜地躺著橘忱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卸奉。 梳的紋絲不亂的頭發(fā)上钝诚,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音榄棵,去河邊找鬼凝颇。 笑死,一個胖子當(dāng)著我的面吹牛疹鳄,可吹牛的內(nèi)容都是我干的拧略。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘪弓,長吁一口氣:“原來是場噩夢啊……” “哼垫蛆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起腺怯,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袱饭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呛占,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虑乖,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年晾虑,在試婚紗的時候發(fā)現(xiàn)自己被綠了疹味。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡帜篇,死狀恐怖糙捺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笙隙,我是刑警寧澤洪灯,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逃沿,受9級特大地震影響婴渡,放射性物質(zhì)發(fā)生泄漏幻锁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一边臼、第九天 我趴在偏房一處隱蔽的房頂上張望哄尔。 院中可真熱鬧,春花似錦柠并、人聲如沸岭接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸣戴。三九已至,卻和暖如春粘拾,著一層夾襖步出監(jiān)牢的瞬間窄锅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工缰雇, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留入偷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓械哟,卻偏偏與公主長得像疏之,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子暇咆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容