当项目工程越来越大之后,大家都会觉得维护起来越来越吃力,如果测试或者UI拿着手机或者截图说这个页面和它的设想有点出入,告诉你是不是他们的姿势不对,哈哈,别想啦,他们是说你的页面有bug了。
如果恰巧这些都是你写的,那么很好的,你可以分分钟找到指定的页面,发现指定的问题,然后帅气的解决。
但是,在很多时候,由于历史遗留等等一系列原因,可能你都不知道这是什么时候写的页面,敲的代码了,但是既然问题都提给你了,你总不能说这个不是我写的,找我们老大吧(这样估计会没有年终奖的啦)。
扯了这么多,接下来说说怎么解决相关的问题呢?其实之前公司就已经考虑到这个问题,做了一个调试摇一摇的功能,但是当时那个功能做的十分的简陋,只能摇一摇显示当前的Activity,如果你的 Activity
上面有事嵌套着 Fragment
的话,那么不好意思,不会显示对应 Fragment
的信息,更别说如果一个 Activity
上面包含了多个 Fragment
的情况。
例如图一中,是在 Activity
中上半部分嵌套了一个 Fragment1
,下半部分又加载了 Fragment2
,在 Fragment2
中是由三个 Fragment
组成的。
需求明确之后,那么就一步一步来完成对应的功能就好啦,结合上面的巴拉巴拉的一堆话,大致需要有以下功能:
1、摇一摇的功能
2、获取当前top的 Activity
3、获取当前 Activity
中显示的 Fragment
4、弹窗显示
5、特殊情况处理
接下来就一个一个开始撸吧。
摇一摇功能
获取当前top的 Activity
这个功能很好实现啦,不管是 Google
还是百度,都能很快的找到想要的代码。相关思路就是获取系统的 SensorManager
,然后注册 SensorManager
的加速度监听器,当加速度大于预设值就响应相关事件。这里只需要注意监听的时机和解除监听的时机。
public void onStart() {
//获取 SensorManager 负责管理传感器
mSensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE));
if (isEnable && mSensorManager != null) {
//获取加速度传感器
Sensor mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mAccelerometerSensor != null) {
mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_UI);
}
}
}
public void onPause() {
if (mSensorManager != null) {
mSensorManager.unregisterListener(this);
}
}
其实到这里,第二个问题 获取当前top的Activity 也解决了。
获取当前 Activity
中显示的 Fragment
获取当前 Activity
中显示的 Fragment
,其实就是说获取到 Activity
中所有的 Fragment
,这个呢,就是使用 FragmentManager
的 manager.getFragments()
方法就好啦,然后在这些 Fragment
中获取到当前正在显示的 Fragment
。这里 Fragment
是否显示是由以下两个方法来判断的:f.isAdded()
、f.isHidden()
。为什么不用一个就好了呢?因为添加 Fragment
可能是 replace()
也有可能是 hide()
, show()
的方式,在 replace()
之后,通过 FragmentManager
还是可以获取到对应的 Fragment
,只是它的 isAdded()
将是 false
,而且此时 isHidden()
也是 false
,所以就会出问题啦! 这里还要考虑 Fragment
嵌套 Fragment
的情况,所以还需要使用到 f.getChildFragmentManager()
的方法递归查询一下。另外如果这里是使用 ViewPager
来模式管理 Fragment
的话,那么这里还需要判断 f.getUserVisibleHint()
的返回情况。
@Nullable
private List<Fragment> getTopFragments() {
if (manager == null) {
return null;
}
List<Fragment> fragments = manager.getFragments();
List<Fragment> topFragments = new ArrayList<>();
if (fragments == null) {
return null;
}
int size = fragments.size();
for (int i = size - 1; i >= 0; i--) {
Fragment f = fragments.get(i);
if (f.isAdded() && !f.isHidden() && f.getUserVisibleHint()) {
Fragment t = getTopFragment(f.getChildFragmentManager());//递归
if (t != null) {
topFragments.add(t);
} else {
topFragments.add(f);
}
}
}
return topFragments;
}
@Nullable
private Fragment getTopFragment(FragmentManager manager) {
List<Fragment> fragments = manager.getFragments();
if (fragments == null) {
return null;
}
int size = fragments.size();
for (int i = size - 1; i >= 0; i--) {
Fragment f = fragments.get(i);
if (f.isAdded() && !f.isHidden() && f.getUserVisibleHint()) {
Fragment tTopFragment = getTopFragment(f.getChildFragmentManager());
return tTopFragment == null ? f : tTopFragment;
}
}
return null;
}
弹出显示
到这里,就可以拿到一个栈顶 Fragment
的集合了,比如说如果是两层嵌套(Activity
-> parentFragment
-> childFragment
)的话,这个集合中就是 childFragment
。那么又怎么回溯上去获取到这么一个层级关系呢?简单说这个就是找爸爸的步骤啦。在 Fragment
中有一个 getParentFragment()
的方法,通过该方法,就可以将爸爸们全部找出来了。只是这里是倒序的(Activity
-> childFragment
-> parentFragment
),我们最终的效果需要是正序的,所以还需要反转一下集合。
List<Fragment> topFragments = getTopFragments();
if (topFragments == null) {
sb.append(context.getClass().getSimpleName());
dialog.setMessage(sb.toString());
dialog.show();
//只有Activity不包含Fragment
return;
}
//从最top的Fragment回溯parent,到了root的时候结束。
ArrayList<Fragment> names;
for (Fragment topFragment : topFragments) {
//Glide 使用Fragment来控制相关的request,不再考虑的范围内
if (GLIDE_FRAGMENT.equals(topFragment.getClass().getName())) {
continue;
}
//先添加Activity的名称
sb.append(context.getClass().getSimpleName());
names = new ArrayList<>();
//倒序找ParentFragment
while (topFragment != null) {
names.add(0, topFragment);//反转顺序
topFragment = topFragment.getParentFragment();
}
int length = names.size();
for (int i = 0; i < length; i++) {
Fragment name = names.get(i);
sb.append("\n");
for (int j = 0; j <= i; j++) {
sb.append(" ");
}
sb.append(name.getClass().getSimpleName());
}
sb.append("\n\n");
}
if (sb.length() == 0) {
sb.append(context.getClass().getSimpleName());
}
dialog.setMessage(sb.toString());
dialog.show();
特殊情况处理
使用 Glide
加载图片的时候,它默认会添加一个没有页面的 SupportRequestManagerFragment
来获取相关生命处理相关的图片加载,所以它是需要我们过滤掉的。
另外,如果某一个页面不需要或者相关功能和摇一摇是冲突的话,这里提供了一个 isEnable
的字段来控制当前页面是否支持摇一摇的功能。
最后,就是我们如何方便调用相关的功能呢?这里对外提供了相关的方法:
private ShakeHelper(Context context) {
this.context = context;
dialog = new AlertDialog.Builder(context).create();
dialog.setOnDismissListener(this);
sb = new StringBuilder();
if (context instanceof FragmentActivity) {
manager = ((FragmentActivity) context).getSupportFragmentManager();
}
}
//摇一摇是否可用,默认可用
public void setEnable(boolean enable) {
isEnable = enable;
}
//获取摇一摇的实例
public static ShakeHelper initShakeHelper(Context context) {
return new ShakeHelper(context);
}
//回调Activity的onStart()
public void onStart() {
//获取 SensorManager 负责管理传感器
mSensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE));
if (isEnable && mSensorManager != null) {
//获取加速度传感器
Sensor mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mAccelerometerSensor != null) {
mSensorManager.registerListener(this, mAccelerometerSensor, SensorManager.SENSOR_DELAY_UI);
}
}
}
//回调Activity的onPause()
public void onPause() {
if (mSensorManager != null) {
mSensorManager.unregisterListener(this);
}
}
相关源码下载
---- Edit By Joe At 2017 02 13 ----
网友评论