當項目工程越來越大之后腌零,大家都會覺得維護起來越來越吃力应役,如果測試或者UI拿著手機或者截圖說這個頁面和它的設想有點出入情组,告訴你是不是他們的姿勢不對,哈哈箩祥,別想啦院崇,他們是說你的頁面有bug了。
如果恰巧這些都是你寫的袍祖,那么很好的底瓣,你可以分分鐘找到指定的頁面,發(fā)現指定的問題盲泛,然后帥氣的解決。
但是键耕,在很多時候寺滚,由于歷史遺留等等一系列原因,可能你都不知道這是什么時候寫的頁面屈雄,敲的代碼了村视,但是既然問題都提給你了,你總不能說這個不是我寫的酒奶,找我們老大吧(這樣估計會沒有年終獎的啦)蚁孔。
扯了這么多奶赔,接下來說說怎么解決相關的問題呢?其實之前公司就已經考慮到這個問題杠氢,做了一個調試搖一搖的功能站刑,但是當時那個功能做的十分的簡陋,只能搖一搖顯示當前的Activity鼻百,如果你的 Activity
上面有事嵌套著 Fragment
的話绞旅,那么不好意思,不會顯示對應 Fragment
的信息温艇,更別說如果一個 Activity
上面包含了多個 Fragment
的情況因悲。
例如圖一中,是在 Activity
中上半部分嵌套了一個 Fragment1
勺爱,下半部分又加載了 Fragment2
晃琳,在 Fragment2
中是由三個 Fragment
組成的。
需求明確之后琐鲁,那么就一步一步來完成對應的功能就好啦卫旱,結合上面的巴拉巴拉的一堆話,大致需要有以下功能:
1绣否、搖一搖的功能
2誊涯、獲取當前top的 Activity
3、獲取當前 Activity
中顯示的 Fragment
4蒜撮、彈窗顯示
5暴构、特殊情況處理
接下來就一個一個開始擼吧。
搖一搖功能
獲取當前top的 Activity
這個功能很好實現啦段磨,不管是 Google
還是百度取逾,都能很快的找到想要的代碼。相關思路就是獲取系統(tǒng)的 SensorManager
,然后注冊 SensorManager
的加速度監(jiān)聽器苹支,當加速度大于預設值就響應相關事件砾隅。這里只需要注意監(jiān)聽的時機和解除監(jiān)聽的時機。
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 ----