自己记录:
1. 有时候我们有多个Activity或dialog,B中需要拿到A中的某个View,但是无法通过传值的方式来实现;
2. B中需要模拟A中某个button的点击事件;
2. 自定义Toast,最大的好处是不会抢走Activity或Dialog的焦点。
不多说,上代码:
首先是Activity:
package com.amuro.dialogtopdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
TestDialogA t = new TestDialogA(MainActivity.this);
t.show();
TestDialogB testDialogB = new TestDialogB(MainActivity.this);
testDialogB.show();
}
});
findViewById(R.id.bt2).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
TestDialogA testDialogA = new TestDialogA(MainActivity.this);
testDialogA.show();
TestToast testToast = new TestToast(MainActivity.this);
testToast.show();
}
});
}
}
我们在这里做一个对比,如果用Dialog覆盖Dialog,下面的dialog是会失去焦点的,而Toast覆盖Dialog就不会。
现在我们要在覆盖在上面的界面上模拟点击下面Dialog上的button,先看底下的DialogA:
package com.amuro.dialogtopdemo;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import java.util.Date;
/**
* Created by Amuro on 16/7/23.
*/
public class TestDialogA extends SDKBaseDialog
{
public TestDialogA(Context context)
{
super(context);
}
@Override
protected void initParams()
{
}
@Override
protected View onCreateContentView()
{
return getContentView();
}
@Override
public void onAttachedToWindow()
{
super.onAttachedToWindow();
}
private boolean hasFocus;
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
this.hasFocus = hasFocus;
Log.e("amuro", hasFocus + "");
// ToastUtils.show(getContext(), hasFocus ? "hasFocus" : "loseFocus");
}
private View getContentView()
{
LinearLayout rootLayout = new LinearLayout(getContext());
LinearLayout.LayoutParams rootParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
rootLayout.setLayoutParams(rootParams);
rootLayout.setBackgroundColor(Color.GREEN);
rootLayout.setOrientation(LinearLayout.VERTICAL);
final Button button = new Button(getContext());
LinearLayout.LayoutParams buttonParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
button.setLayoutParams(buttonParams);
button.setText("Test");
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
ToastUtils.show(getContext(), "Test: " + hasFocus);
}
});
Button buttonMock = new Button(getContext());
LinearLayout.LayoutParams buttonMockParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
buttonMock.setLayoutParams(buttonMockParams);
buttonMock.setText("Mock");
buttonMock.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
}
});
rootLayout.addView(button);
rootLayout.addView(buttonMock);
return rootLayout;
}
}
再分别看覆盖在上面的DialogB和Toast
package com.amuro.dialogtopdemo;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Created by Amuro on 16/7/23.
*/
public class TestDialogB extends SDKBaseDialog
{
public TestDialogB(Context context)
{
super(context);
}
private MockTool mockTool;
@Override
protected void initParams()
{
mockTool = new MockTool();
buttonConfirmPay = mockTool.getConfirmPayButton(context);
}
private Button buttonConfirmPay;
@Override
protected View onCreateContentView()
{
LinearLayout rootLayout = new LinearLayout(getContext());
LinearLayout.LayoutParams rootParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
rootLayout.setLayoutParams(rootParams);
rootLayout.setBackgroundColor(Color.WHITE);
rootLayout.setOrientation(LinearLayout.VERTICAL);
TextView textView = new TextView(getContext());
final LinearLayout.LayoutParams buttonParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
textView.setLayoutParams(buttonParams);
textView.setText("请等待。。。。。。");
Button button = new Button(getContext());
button.setLayoutParams(
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
button.setText("button");
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
mockTool.mockClick(buttonConfirmPay);
}
});
rootLayout.addView(textView);
rootLayout.addView(button);
return rootLayout;
}
}
package com.amuro.dialogtopdemo;
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Created by Amuro on 16/7/26.
*/
public class TestToast
{
private Context context;
private MockTool mockTool;
private Button buttonConfirmPay;
public TestToast(Context context)
{
this.context = context;
mockTool = new MockTool();
buttonConfirmPay = mockTool.getConfirmPayButton(context);
}
private Toast toast;
private View toastView;
private Object mTN;
public void show()
{
if(toast == null)
{
// 先创建一个Toast对象
toast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
// 设置Toast信息提示框显示的位置(在屏幕顶部水平居中显示)
toast.setGravity(Gravity.CENTER, 0, 0);
}
try
{
if(mTN == null)
{
Field fdToastView = toast.getClass().getDeclaredField("mNextView");
fdToastView.setAccessible(true);
toastView = (View) fdToastView.get(toast);
toastView = getToastView();
// 从Toast对象中获得mTN变量
Field field = toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
mTN = field.get(toast);
}
Field fdTNView = mTN.getClass().getDeclaredField("mNextView");
fdTNView.setAccessible(true);
fdTNView.set(mTN, toastView);
// TN对象中获得了show方法
Method method = mTN.getClass().getDeclaredMethod("show", null);
// 调用show方法来显示Toast信息提示框
method.invoke(mTN, null);
}
catch (Exception e)
{
e.printStackTrace();
}
}
private View getToastView()
{
LinearLayout rootLayout = new LinearLayout(context);
LinearLayout.LayoutParams rootParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
rootLayout.setLayoutParams(rootParams);
rootLayout.setBackgroundColor(Color.GREEN);
rootLayout.setOrientation(LinearLayout.VERTICAL);
final TextView textView = new TextView(context);
LinearLayout.LayoutParams buttonParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
textView.setLayoutParams(buttonParams);
textView.setText("请等待");
ProgressBar progressBar = new ProgressBar(context);
progressBar.setLayoutParams(
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
final Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
mockTool.mockClick(buttonConfirmPay);
}
}, 2000);
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
hide();
}
}, 5000);
rootLayout.addView(textView);
rootLayout.addView(progressBar);
return rootLayout;
}
public void hide()
{
try
{
// TN对象中获得了show方法
Method method = mTN.getClass().getDeclaredMethod("hide", null);
method.setAccessible(true);
// 调用show方法来显示Toast信息提示框
method.invoke(mTN, null);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
DialogB和Toast中都实现了模拟点击,测试发现DialogB的时候下面的A是没有焦点的,而Toast的时候是有焦点的;
来看模拟点击和获取button的代码:
package com.amuro.dialogtopdemo;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* Created by Amuro on 16/7/23.
*/
public class MockTool
{
public void mockClick(Button button)
{
int[] location = new int[2];
button.getLocationOnScreen(location);
long time = new Date().getTime();
MotionEvent motionEvent =
MotionEvent.obtain(
time,
time,
MotionEvent.ACTION_DOWN,
location[0] + 1,
location[0] + 1,
0);
button.dispatchTouchEvent(motionEvent);
motionEvent.recycle();
motionEvent =
MotionEvent.obtain(
time + 1,
time + 1,
MotionEvent.ACTION_UP,
location[0] + 1,
location[0] + 1,
0);
button.dispatchTouchEvent(motionEvent);
motionEvent.recycle();
}
private Button buttonConfirmPay;
public Button getConfirmPayButton(Context context)
{
Field wmGlobalField = null;
try
{
wmGlobalField =
context.getSystemService(Context.WINDOW_SERVICE).
getClass().getDeclaredField("mGlobal");
}
catch (Exception e)
{
wmGlobalField = null;
}
if (wmGlobalField != null)
{
try
{
wmGlobalField.setAccessible(true);
Object wmGlobal = wmGlobalField.get(context.getSystemService(Context.WINDOW_SERVICE));
Field viewsField = wmGlobal.getClass().getDeclaredField("mViews");
viewsField.setAccessible(true);
ArrayList<View> views = (ArrayList<View>) viewsField.get(wmGlobal);
getConfirmPayButton(views);
return buttonConfirmPay;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
else
{
try
{
Field wmLocalField =
context.getSystemService(Context.WINDOW_SERVICE).getClass().
getSuperclass().getDeclaredField("mWindowManager");
// context.getSystemService(Context.WINDOW_SERVICE).
// getClass().getDeclaredField("mWindowManager");
wmLocalField.setAccessible(true);
Object wmLocal =
wmLocalField.get(context.getSystemService(Context.WINDOW_SERVICE));
Field viewsField = wmLocal.getClass().getDeclaredField("mViews");
viewsField.setAccessible(true);
List<View> viewList = Arrays.asList((View[])viewsField.get(wmLocal));
ArrayList<View> views = new ArrayList<>();
for(View view : viewList)
{
views.add(view);
}
getConfirmPayButton(views);
return buttonConfirmPay;
}
catch (Exception e)
{
return null;
}
}
}
private void getConfirmPayButton(ArrayList<View> views)
{
for (int i = 0; i < views.size(); i++)
{
ViewGroup viewGroup = (ViewGroup) views.get(i);
traverse(viewGroup);
if (buttonConfirmPay != null)
{
break;
}
}
}
private void traverse(ViewGroup viewGroup)
{
int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++)
{
View view = viewGroup.getChildAt(i);
if (view instanceof Button)
{
if (((Button) view).getText().equals("Test"))
{
buttonConfirmPay = (Button) view;
break;
}
}
if (view instanceof ViewGroup)
{
traverse((ViewGroup) view);
}
}
}
}
大量的反射,模拟点击的原理就是把down和up事件传给button,让button的onclick事件被触发;
从Window中获取button的原理也是从window中拿到所有被缓存的view,然后从中间找到你要的那个控件就行了。
就酱~
本文介绍了一种在Android应用中模拟按钮点击的方法,并展示了如何自定义Toast使其不会抢走Activity或Dialog的焦点。通过反射机制,实现了跨界面的交互控制。
664

被折叠的 条评论
为什么被折叠?



