title: Android 事件统计


1.写在前面的话


2.反射和代理

package com.nick.model;

//定义了一个实体类UserModel
public class UserModel {
private String userName;
private String password;
private UserInfoModel userInfoModel; public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public UserInfoModel getUserInfoModel() {
return userInfoModel;
} public void setUserInfoModel(UserInfoModel userInfoModel) {
this.userInfoModel = userInfoModel;
} @Override
public String toString() {
String result = "userName = " + userName + " password = " + password + " " + userInfoModel.toString();
return result;
}
}
package com.nick.model;

public class UserInfoModel {
private int age;
private String birth; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getBirth() {
return birth;
} public void setBirth(String birth) {
this.birth = birth;
} @Override
public String toString() {
return "age = " + age + " birth = " + birth;
}
}
public static void main(String[] args) {
UserInfoModel userInfoModel = new UserInfoModel();
userInfoModel.setAge(10);
userInfoModel.setBirth("2017-03-17 17:08:56");
UserModel userModel = new UserModel();
userModel.setUserName("小红");
userModel.setPassword("password");
userModel.setUserInfoModel(userInfoModel); System.out.println(userModel.toString()); // 通过反射修改属性
try {
Class userModelRe = Class.forName(UserModel.class.getName());
Field userName = userModelRe.getDeclaredField("userName");
userName.setAccessible(true);// setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
userName.set(userModel, "小明");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(userModel.toString());
}

3. 准备工作

    public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
    ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
} static class ListenerInfo {
protected OnFocusChangeListener mOnFocusChangeListener; private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners; protected OnScrollChangeListener mOnScrollChangeListener; private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners; public OnClickListener mOnClickListener; protected OnLongClickListener mOnLongClickListener; protected OnContextClickListener mOnContextClickListener; protected OnCreateContextMenuListener mOnCreateContextMenuListener; private OnKeyListener mOnKeyListener; private OnTouchListener mOnTouchListener; private OnHoverListener mOnHoverListener; private OnGenericMotionListener mOnGenericMotionListener; private OnDragListener mOnDragListener; private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener; OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
}
    private final class PerformClick implements Runnable {
@Override
public void run() {
performClick();
}
} public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
} sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
public boolean onTouchEvent(MotionEvent event) {

    if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
} if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
} if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback(); // Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
} if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
} if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
} removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
...
...
}

4.代码实现

public class HookUtils {
private static final String VIEW_CLASS = "android.view.View"; /**
* @param mActivity
* @param onClickListener
*/
public static void hookListener(Activity mActivity, OnClickListener onClickListener) {
if (mActivity != null) {
View decorView = mActivity.getWindow().getDecorView();
getView(decorView, onClickListener);
}
} /**
* 递归进行viewHook
* @param view
* @param onClickListener
*/
private static void getView(View view, OnClickListener onClickListener) {
//递归遍历,判断当前view是不是ViewGroup,如果是继续遍历,知道不是为止
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
getView(((ViewGroup) view).getChildAt(i), onClickListener);
}
}
viewHook(view, onClickListener);
} /**
* 通过反射将我们的代理类替换原来的onClickListener
*
* @param view
* @param onClickListener
*/
private static void viewHook(View view, OnClickListener onClickListener) {
try {
Class viewClass = Class.forName(VIEW_CLASS);//反射创建View
Field listenerInfoField = viewClass.getDeclaredField("mListenerInfo");//获得View属性mListenerInfo
listenerInfoField.setAccessible(true);
Object mListenerInfo = listenerInfoField.get(view);//ListenerInfo==>>View对象中的mListenerInfo的实例 if (mListenerInfo != null) {
Class listenerInfo2 = Class.forName("android.view.View$ListenerInfo");//反射创建ListenerInfo
Field onClickListenerFiled = listenerInfo2.getDeclaredField("mOnClickListener");//获得ListenerInfo属性mOnClickListener
onClickListenerFiled.setAccessible(true);
View.OnClickListener o1 = (View.OnClickListener) onClickListenerFiled.get(mListenerInfo);//获得mListenerInfo的实例中的mOnClickListener实例
if (o1 != null) {
View.OnClickListener onClickListenerProxy = new OnClickListenerProxy(o1, onClickListener);
onClickListenerFiled.set(mListenerInfo, onClickListenerProxy);//设置ListenerInfo属性mOnClickListener为我们的代理listener
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} public interface OnClickListener {
void beforeInListener(View v);
void afterInListener(View v);
} private static class OnClickListenerProxy implements View.OnClickListener {
private View.OnClickListener object;
private HookUtils.OnClickListener mListener; public OnClickListenerProxy(View.OnClickListener object, HookUtils.OnClickListener listener) {
this.object = object;
this.mListener = listener;
} @Override
public void onClick(View v) {
if (mListener != null) {
mListener.beforeInListener(v);
}
if (object != null) {
object.onClick(v);
}
if (mListener != null) {
mListener.afterInListener(v);
}
}
}

5.测试

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View view = findViewById(R.id.tv_1);
view.setTag("1");
view.setOnClickListener(this);
View view1 = findViewById(R.id.tv_2);
view1.setTag("2");
view1.setOnClickListener(this);
View view2 = findViewById(R.id.tv_3);
view2.setTag("3");
view2.setOnClickListener(this);
HookUtils.hookListener(this, this);//要在setOnxxxListener之后调用
} @Override
public void onClick(View v) {
Log.d("fxxk", "点击id=" + v.getId() + "v===" + v.getTag().toString());
} @Override
public void beforeInListener(View v) {
Log.d("fxxk", "点击前id=" + v.getId() + "v===" + v.getTag().toString());
} @Override
public void afterInListener(View v) {
Log.d("fxxk", "点击后id=" + v.getId() + "v===" + v.getTag().toString());
}

6. 写在最后

05-06 05:01