欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Fragment監(jiān)聽返回鍵的一種合理方式

 更新時(shí)間:2020年11月10日 10:10:19   作者:Andme  
這篇文章主要給大家介紹了關(guān)于Android Fragment監(jiān)聽返回鍵的一種合理方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

開場

以下場景為杜撰:

產(chǎn)品經(jīng)理:“小羅,這個(gè)信息發(fā)送界面,如果用戶輸入了內(nèi)容,點(diǎn)擊返回鍵的時(shí)候,要先詢問用戶是否保存草稿箱哈”。

小羅:“收到,這問題簡單?!?/p>

說完小羅就準(zhǔn)備著手處理,然后卻發(fā)現(xiàn)信息編輯界面是一個(gè)Fragment,然而Fragment并沒有提供返回鍵點(diǎn)擊的直接處理;小羅雖菜,但是摸魚也摸了些年頭了,這問題難不倒小羅。

小羅心想,反正Activity提供了onBackPressed方法,再不濟(jì)的情況把這個(gè)操作分發(fā)到Fragment中去就好,可是對于處女座的小羅來說,在解決問題的基礎(chǔ)上,起碼代碼要寫的漂亮一點(diǎn),寫的漂亮一點(diǎn)心里就舒服一點(diǎn),心里舒服一點(diǎn)就...(此處內(nèi)容很長)。

小羅堅(jiān)信“條條大路通羅馬”,我們不僅要到羅馬,還要風(fēng)風(fēng)光光的去,所以對于“Fragment如何監(jiān)聽返回鍵的點(diǎn)擊”,小羅決定下點(diǎn)功夫;

為什么關(guān)注的點(diǎn)是Fragment去監(jiān)聽返回鍵,而不是其他?其實(shí)在現(xiàn)在的開發(fā)過程中,F(xiàn)ragment的使用比重是非常大的,對于個(gè)人而言,幾乎整個(gè)工程的界面實(shí)現(xiàn)都是基于Fragment而非Activity。

一、最lowB的方式(不推薦)

這就是小羅心里的預(yù)備方案,在實(shí)在沒有辦法的時(shí)候會(huì)采用此方法,也就是前面提到的,我們可以在Activity執(zhí)行onBackPressed時(shí),分發(fā)到Fragment中去;那我們用什么來分發(fā)呢?這個(gè)分發(fā)就好比是連接Activity和Fragment之間的一個(gè)紐帶,雙方均能夠訪問到這個(gè)對象就可以了,所以一個(gè)可以的選擇之一是使用ViewModel,當(dāng)然還可以有其他選擇,在此就不細(xì)聊了。

二、使用OnKeyListener(不推薦)

這種方式可能不常用,不容易想到這方面,所以這種方式也不推薦,簡單做個(gè)了解;

通過設(shè)置View的OnKeyListener來監(jiān)聽返回鍵的處理,此方法也沒什么大的弊端,只是要注意以下兩點(diǎn):

1、如果把這個(gè)功能封裝在Fragment基類中的話,可能存在被覆蓋的問題;比如在基類中設(shè)置了OnKeyListener,而子類也需要設(shè)置OnKeyListener,此時(shí)設(shè)置的監(jiān)聽則會(huì)替換默認(rèn)設(shè)置的監(jiān)聽,從而導(dǎo)致意想不到的可能,不過此問題幾乎不太可能發(fā)生。

2、需要注意這種方式將會(huì)改變返回鍵處理的順序,也就是會(huì)先處理OnKeyListener的回調(diào),再處理Activity的onBackPressed,所以要注意這個(gè)關(guān)系。

三、Jetpack提供的方式

其實(shí)對于返回鍵的分發(fā),官方已經(jīng)做了支持,在Activity中提供了一個(gè)用于分發(fā)返回鍵事件的對象,通過調(diào)用Activity的getOnBackPressedDispatcher()方法得到這個(gè)對象,由于這個(gè)對象是在比較底層的androidx.activity.ComponentActivity中提供的(AppCompatActivity->FragmentAcitivty->androidx.activity.ComponentActivity),所以在Fragment中可以直接拿到這個(gè)對象添加回調(diào);

官方資料入口

//官方使用示例 
public class FormEntryFragment extends Fragment {
 @Override
 public void onAttach(@NonNull Context context) {
  super.onAttach(context);
  //定義回調(diào)
  OnBackPressedCallback callback = new OnBackPressedCallback(
   true // default to enabled
  ) {
   @Override
   public void handleOnBackPressed() {
    showAreYouSureDialog();
   }
  };
  
  //獲取Activity的返回鍵分發(fā)器添加回調(diào)
  requireActivity().getOnBackPressedDispatcher().addCallback(
   this, // LifecycleOwner
   callback);
 }
}

簡單明了,這個(gè)事情好像到此為止了~~

但隨著深入了解,事情似乎沒有這么簡單,經(jīng)過源碼分析和資料收集,發(fā)現(xiàn)如果直接使用會(huì)存在以下弊端:

1、Fragment回調(diào)處理時(shí),無法向上傳遞

2、回調(diào)是否可用需要主動(dòng)標(biāo)記,而非運(yùn)行時(shí)確定

簡單說一下OnBackPressedDispatcher分發(fā)返回鍵的流程:

	//官方源碼
 @MainThread
 public void onBackPressed() {
  Iterator<OnBackPressedCallback> iterator =
    mOnBackPressedCallbacks.descendingIterator();
  while (iterator.hasNext()) {
   OnBackPressedCallback callback = iterator.next();
   if (callback.isEnabled()) {
    callback.handleOnBackPressed();
    return;
   }
  }
  if (mFallbackOnBackPressed != null) {
   mFallbackOnBackPressed.run();
  }
 }

當(dāng)分發(fā)返回鍵事件時(shí),會(huì)倒序循環(huán)遍歷已經(jīng)注冊的回調(diào),如果回調(diào)isEnabled設(shè)置為true,則執(zhí)行回調(diào)的方法,分發(fā)結(jié)束;

那前面提到的弊端是怎么產(chǎn)生的呢?假如一個(gè)Activity有兩個(gè)Fragment A和B,均注冊了返回鍵點(diǎn)擊事件(有童鞋會(huì)說了,這種場景不太可能存在,確實(shí),這種場景是不多,但不代表沒有,做一些了解也不是壞事),并且兩個(gè)回調(diào)的isEnabled均設(shè)置為true,那么當(dāng)分發(fā)事件時(shí),會(huì)將事件分發(fā)給B,但是B此時(shí)并不需要處理返回鍵事件,但是B又沒有辦法再繼續(xù)將事件傳遞給A了;

“你傻啊,你B不執(zhí)行返回鍵事件,就設(shè)置isEnable為false啊”

“是啊,B不執(zhí)行事件是該設(shè)置為false,可是我怎么知道什么時(shí)候去把它設(shè)置成false?難道動(dòng)態(tài)綁定判斷條件的值進(jìn)行設(shè)置么?”

轉(zhuǎn)頭一想“咦,好像確實(shí)可以動(dòng)態(tài)修改回調(diào)的isEnabled值呢,將回調(diào)的值跟一個(gè)LiveData綁定不就可以了么!”
理是這個(gè)理,但是我不愿意做額外的工作,我不愿這么干,誰知道動(dòng)態(tài)判斷條件到底有多復(fù)雜呢,難道我不可以在返回鍵點(diǎn)擊的時(shí)候去判斷么?

四、靈機(jī)一動(dòng),官方升級(jí)版(推薦方式)

官方的方式不是存在上面兩個(gè)弊端么,解決這兩個(gè)問題不就好了;所以結(jié)合官方OnBackPressedDispatcher和OnKeyListener兩者的優(yōu)點(diǎn),創(chuàng)建了andme.arch.activity.AMBackPressedDispatcher,在保留官方原有的功能的同時(shí),更改事件分發(fā)流程,并將返回鍵持有者一并傳入,用于解決一些更復(fù)雜一點(diǎn)的需求;

 @MainThread
 fun onBackPressed(): Boolean {
  if (!hasRegisteredCallbacks())
   return false

  val iterator = mOnBackPressedCallbacks.descendingIterator()
  while (iterator.hasNext()) {
   val callback = iterator.next()
   //判斷回調(diào)是否需要消耗事件在決定是否繼續(xù)傳遞
   if (callback.handleOnBackPressed(owner)) {
    return true
   }
  }
  return false
 }

五、官方使用技巧版

這種方法其實(shí)是我在發(fā)布文章之后,群友提供的一種思路,說實(shí)話,非常有技巧,剛開始看到的時(shí)候眼前一亮;其核心原理是默認(rèn)注冊的回調(diào)是可用的,在回調(diào)執(zhí)行中,先判斷自己是否需要執(zhí)行回調(diào),如果不需要執(zhí)行回調(diào),則將自己的isEnabled設(shè)置為false,然后再調(diào)用OnBackPressedDispatcher重新分發(fā)返回鍵事件(由于此時(shí)已將自己設(shè)置為false,此時(shí)便不會(huì)響應(yīng)回調(diào)),調(diào)用方法之后再將isEnabled設(shè)置為true,巧用了遞歸,該方式不錯(cuò)的;

最開始群友提供的代碼有一丟丟瑕疵,以下為修正之后的代碼,在Fragment中定義這兩個(gè)方法,在需要綁定返回鍵監(jiān)聽的時(shí)候調(diào)用這個(gè)兩個(gè)方法之一即可(推薦調(diào)用與生命周期相關(guān)的方法);

fun addOnBackPressed(onBackPressed: () -> Boolean): OnBackPressedCallback {
  val callback = object : OnBackPressedCallback(true) {
   override fun handleOnBackPressed() {
    if (!onBackPressed()) {
     isEnabled = false
     requireActivity().onBackPressedDispatcher.onBackPressed()
     isEnabled = true
    }
   }
  }
  requireActivity().onBackPressedDispatcher.addCallback(callback)
  return callback
 }

 fun addOnBackPressed(owner: LifecycleOwner, onBackPressed: () -> Boolean): OnBackPressedCallback {
  val callback = object : OnBackPressedCallback(true) {
   override fun handleOnBackPressed() {
    if (!onBackPressed()) {
     isEnabled = false
     requireActivity().onBackPressedDispatcher.onBackPressed()
     isEnabled = true
    }
   }
  }
  requireActivity().onBackPressedDispatcher.addCallback(owner,callback)
  return callback
 }

但是經(jīng)過慎重思考,最終我還是沒有用這種方法,雖然這種方法在幾乎百分之八九十的情況下是沒有問題的,但是我認(rèn)為可能還是有場景無法滿足;

舉個(gè)例子,一個(gè)Activity添加了一個(gè)Fragment,這個(gè)Fragment又順序添加了A和B兩個(gè)ChildFragment,那在B執(zhí)行返回處理的時(shí)候,是想回到A還是finish呢?或者是其他呢,也是就是說我們無法確定,在Fragment執(zhí)行返回鍵處理時(shí),是否需要直接調(diào)用Activity.super.onBackPressed方法的可能。

我們永遠(yuǎn)無法預(yù)估用戶的場景到底有多復(fù)雜,需求有多變態(tài),所以盡可能的考慮把。

總結(jié)

綜上所述,我目前還是會(huì)繼續(xù)使用第四種我寫的方案,第五種方案也推薦,畢竟在絕大部分場景中都是沒有問題的
那么我們考慮第四種方案到底是否可行?

1、功能性

滿足了功能需求,并且至少目前是沒有想到有任何可能出現(xiàn)問題的場景

2、侵入性

幾乎對用戶場景沒什么影響吧,只是對用戶提供了一個(gè)可見的處理返回鍵事件的方法而已

3、替換性

如果采用第四種方案,要更換成第五種方案,容易么?一兩句代碼的事情而已

或者更換成其他方案容易么?也是一兩句代碼的的事情而已

并且即便替換成其他方案,也不會(huì)對現(xiàn)有系統(tǒng)造成任何影響,因?yàn)閷τ贔ragment監(jiān)聽返回鍵這個(gè)需求來講,這個(gè)需求的核心就是需要一個(gè)在Fragment中處理返回鍵事件的方法而已,其他東西對用戶來講都是無感的
所以總體覺得沒什么毛?。?/p>

如果你有更好的思路,歡迎溝通,不勝感激;

另外,上述功能其實(shí)并不僅僅支持在Fragment中處理返回鍵事件,理論上來說任何想要監(jiān)聽返回鍵處理的都可以通過Activity獲取AMBackPressedDispatcher對象添加回調(diào)即可。

Andme Github地址

到此這篇關(guān)于Android Fragment監(jiān)聽返回鍵的一種合理方式的文章就介紹到這了,更多相關(guān)Android Fragment監(jiān)聽返回鍵內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論