最近在研究埋点统计,经过认真调研,整理出了我们自己的一套埋点方案,采用统计所有view点击事件的方案。由于网上内容有很多错误或不完整,本文将所有相关技术分析和代码完整的贴出来方便大家直接用,可以绕开好多弯路。
本文解决一大问题是在有Fragment切换的时候,Fragment中的view与上一个Fragment中的view出现了冲突的问题,即在viewPage中会预先加载好下一个Fragment,造成点击事件统计冲突。view.isShown()用于判断fragment是否为当前展示的那一个。
BaseActivity中代码
//统计埋点
LinkedHashSet<View> views = new LinkedHashSet<View>();
public LinkedHashSet<View> allView = new LinkedHashSet<View>();
int statusBarHeight;
private String mClassName = this.getClass().getSimpleName();
//分发点击事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View view = getCurrentFocus();
if (isHideInput(view, ev)) {
HideSoftInput(view.getWindowToken());
}
}else if(ev.getAction() == MotionEvent.ACTION_UP){
/*获取当前点击位置,遍历布局,获取当前点击位置对应的view,根据view映射路径,与json文件中的对比*/
eventInViewCollect( ev);
}
return super.dispatchTouchEvent(ev);
}
@Override
public void onWindowFocusChanged (boolean hasFocus){
super.onWindowFocusChanged(hasFocus);
if(!hasFocus) return; //失去焦点时 不执行
collectView();//收集统计用点击view的信息
}
public void eventInViewCollect(MotionEvent event) {
if (!HengChangAgent.isCollectMode()) return; //非收集模式 退出
double x = event.getRawX();
double y = event.getRawY() - statusBarHeight;
List touchViewid = new ArrayList<String> ();
for(View view :allView) {
Rect outRect = new Rect();
view.getGlobalVisibleRect(outRect);
if(outRect.contains((int)x ,(int)y )){
String idName = ViewUtils.getResourceName(this,view.getId());
// view.isShown() view在上层才获取其id 针对多个fragment切换情况
if(!TextUtils.isEmpty(idName) && view.isShown()){
touchViewid.add(idName);
}
if (touchViewid.size()>0){
LogUtils.d("HengChangAgent" , "这是我们的埋点:"+touchViewid.toString());
String json= "";
try {
JSONObject jsonObj = new JSONObject();//对象,json形式
jsonObj.put("idName", touchViewid.toString());//向对象里面添加值
json = jsonObj.toString();
}
catch (Exception e)
{
LogUtils.d("out" , "埋点json生成失败");
}
HengChangAgent.requestBuriedPointStatistics(/*AppAccount.getInstance().getUserid()+*/"",json);
}
}
}
}
/**
* 收集统计用点击view的信息
*/
public void collectView() {
if (!HengChangAgent.isCollectMode()) return; //非收集模式 退出
ViewGroup rootView = ViewUtils.getRootFrame(this);
allView = getView(rootView); //获取到
//过滤掉没有定义过id的view
LinkedHashSet<View> tempView = new LinkedHashSet<View>();
for (View view :allView){
String idName = ViewUtils.getResourceName(this,view.getId());
if(!TextUtils.isEmpty(idName)){
tempView.add(view);
}
}
allView = tempView;
LogUtils.d("out" , allView.size()+"");
Rect outRect = new Rect();
this.getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
statusBarHeight = outRect.top;
}
/*获取所有View和没有子View的ViewGroup*/
public LinkedHashSet<View> getView(ViewGroup viewGroup){
if(viewGroup == null) return null;
int count = viewGroup.getChildCount();
for(int i=0;i<count;i++){
View view = viewGroup.getChildAt(i);
if(!(view instanceof ViewGroup)){
views.add(viewGroup);
views.add(view);
}else if(view instanceof ViewGroup){
getView((ViewGroup)view);
}else{
break;
}
}
return views;
}
BaseFragment中的onActivityCreated()添加如下代码
/**
* 收集统计用点击view的信息
*/
public void collectView() {
if (!HengChangAgent.isCollectMode()) return;//非收集模式 退出
if (getActivity() instanceof BaseActivity ){
ViewGroup rootView = ViewUtils.getRootFrame(getActivity());//获取根viewGroup
LinkedHashSet<View> views = ((BaseActivity)getActivity()).getView(rootView);
if (views ==null || views.size()==0 ) return;
//过滤掉没有定义过id的view
LinkedHashSet<View> filterViews = new LinkedHashSet<View>();
for (View view :views){
String idName = ViewUtils.getResourceName(getActivity(),view.getId());
if(!TextUtils.isEmpty(idName) && idName.contains(":id/")){
filterViews.add(view);
}
}
((BaseActivity)getActivity()).allView.addAll(filterViews);
}
}
相关工具类
public class ViewUtils {
//获取Activity的根view
@NonNull
public static FrameLayout getRootFrame(@NonNull Activity activity) {
View re = activity.findViewById(android.R.id.content);
if (re != null && re instanceof FrameLayout) {
return (FrameLayout) re;
}
ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
re = viewGroup.getChildAt(viewGroup.getChildCount() - 1);
if (re != null && re instanceof FrameLayout) {
return (FrameLayout) re;
} else {
re = new FrameLayout(activity);
activity.getActionBar().getHeight();
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.MATCH_PARENT);
viewGroup.addView(re, lp);
return (FrameLayout) re;
}
}
/**
* 获取资源文件名字
* @param resid
* @return
*/
public static String getResourceName(@NonNull Context context , @AnyRes int resid){
String res = "";
if (-1 ==resid ) return res;
try {
res = context.getResources().getResourceName(resid);
} catch (Resources.NotFoundException e) {
// e.printStackTrace();
// LogUtils.d("out", "未获取到ResourceName ---id="+resid);
}
return res;
}
//获取Activity的根view
public static ViewGroup getRootView(Activity context)
{
return (ViewGroup)((ViewGroup)context.findViewById(android.R.id.content)).getChildAt(0);
}
}
参考文献
http://blog.csdn.net/HHcoco/article/details/52250627
http://blog.csdn.net/fkaking/article/details/50338467
http://blog.csdn.net/hhcoco/article/details/52243079