2017-10-09 55 views
4

TL; DR:如果使用數據綁定使用的佈局有一個EditText,並且對於android:text綁定表達式,綁定表達式將覆蓋保存的實例狀態值......即使我們沒有明確觸發約束力的評估。用戶在配置更改前輸入的內容被清除。我們如何解決這個問題,以便在配置更改時使用保存的實例狀態值?我們如何獲取數據綁定以使用保存的實例狀態?


我們有一個愚蠢的Model

public class Model { 
    public String getTitle() { 
    return("Title"); 
    } 
} 

我們有一個佈局引用Model

<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto"> 

    <data> 

    <variable 
     name="model" 
     type="com.commonsware.databindingstate.Model" /> 
    </data> 

    <android.support.constraint.ConstraintLayout xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    tools:context="com.commonsware.databindingstate.MainActivity"> 

    <EditText android:id="@+id/title" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:inputType="text" 
     app:layout_constraintLeft_toLeftOf="parent" 
     app:layout_constraintRight_toRightOf="parent" 
     app:layout_constraintTop_toTopOf="parent" /> 

    </android.support.constraint.ConstraintLayout> 
</layout> 

注意,這個佈局沒有綁定表達式;我們會做一點。

的佈局是在動態片段中使用:

public class FormFragment extends Fragment { 
    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, 
          @Nullable ViewGroup container, 
          @Nullable Bundle savedInstanceState) { 
    return(MainBinding.inflate(inflater, container, false).getRoot()); 
    } 
} 

請注意,我們不是要求setModel()任何地方的Model推入約束力。 MainBinding(上面顯示的main.xml佈局)僅用於充氣佈局。

此代碼(使用合適的FragmentActivity來設置FormFragment)可以正確使用保存的實例狀態。如果用戶鍵入的東西到EditText,然後旋轉屏幕,新近重新EditText顯示輸入型文本。

現在,讓我們改變佈局,添加綁定表達式android:text

<?xml version="1.0" encoding="utf-8"?> 
<layout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto"> 

    <data> 

    <variable 
     name="model" 
     type="com.commonsware.databindingstate.Model" /> 
    </data> 

    <android.support.constraint.ConstraintLayout xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    tools:context="com.commonsware.databindingstate.MainActivity"> 

    <EditText android:id="@+id/title" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:inputType="text" 
     android:text="@{model.title}" 
     app:layout_constraintLeft_toLeftOf="parent" 
     app:layout_constraintRight_toRightOf="parent" 
     app:layout_constraintTop_toTopOf="parent" /> 

    </android.support.constraint.ConstraintLayout> 
</layout> 

現在,如果用戶鍵入的東西到EditText和旋轉屏幕,新近重新EditText是空的。綁定表達式覆蓋從已保存實例狀態恢復的任何框架。

這涉及儘管我不是呼籲結合setModel()。我當然能看到,如果我在結合稱爲setModel()其中,將與來自模型的數據替換EditText內容。但我沒有那樣做。

我可以重現兩種官方設備(谷歌像素,安卓8.0)和生態系統設備(三星Galaxy S8,安卓7.1)此行爲。

這可以通過自己保存狀態並在某個時刻恢復它來解決「手動」問題。例如,多個註釋建議使用雙向綁定,但與其他設計目標(例如不可變模型對象)背道而馳。這似乎是數據綁定的一個相當根本的限制,所以我希望有一些我錯過了,我可以配置自動使用保存的實例狀態。

+0

在'機器人:文本=「@ {} model.title」'您使用的單向數據綁定,或者它是一個錯字和你的意思是雙向數據綁定? – pskink

+0

@pskink:我正在使用單向綁定。雙向綁定將是另一種可能的解決方法,但我不希望在真正的應用程序中使用該問題。雙向綁定是「自己拯救國家並在某個時候恢復」的另一種變體。 – CommonsWare

+2

查看使用雙向綁定自動狀態恢復的[相關答案](https://stackoverflow.com/a/46086436/1676363)。 – ianhanniballake

回答

2

我以爲ianhanniballake有一個相關答案的參考,但也許還有更多。這是我對如何適用這些情況的解釋。

使用您提供的XML,以下代碼將交替地從保存的實例狀態中恢復並從模型恢復。當保存的實例狀態恢復時,大概沒有模型實例化來恢復。這是當mCount是偶數。如果存在模型,則基本忽略保存的實例狀態,並且綁定接管。這裏有一點比我們想要的更多的邏輯,但它不如顯式保存和恢復。

mCount僅僅是爲了論證的緣故。將使用模型是否存在的標誌或其他指示。

public class MainActivity extends AppCompatActivity { 
    private ActivityMainBinding binding; 
    private int mCount; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 
     mCount = (savedInstanceState == null) ? 0 : savedInstanceState.getInt("mCount", 0); 
     if (mCount % 2 == 1) { 
      // 1st, 3rd, 5th, etc. rotations. Explicitly execute the bindings and let the framework 
      // restore from the saved instance state. 
      binding.executePendingBindings(); 
     } else { 
      // First creation and 2nd, 4th, etc. rotations. Set up our model and let the 
      // framework restore from the saved instance state then overwrite with the bindings. 
      // (Or maybe it just ignores the saved instance state and restores the bindnings.) 
      Model model = new Model(); 
      binding.setModel(model); 
     } 
     mCount++; 
    } 

    @Override 
    public void onSaveInstanceState(Bundle bundle) { 
     super.onSaveInstanceState(bundle); 
     bundle.putInt("mCount", mCount); 
    } 
} 
+0

「我以爲ianhanniballake提到了一個相關的答案」 - 我不希望雙向約束,正如我在評論中指出的那樣,在編輯問題和賞金時。 「當保存的實例狀態恢復時,假設沒有模型實例化來恢復」 - 事實並非如此。通過單向綁定,我們可能已經初始化了模型中的小部件,但是保存的實例狀態應該接管,以便在設備進行配置更改時不會丟失用戶輸入的修改。 – CommonsWare

+0

但是,我可以確認'executePendingBindings()',至少在從一個活動的'onCreate()'方法調用時,似乎改善了問題,允許正常保存的實例狀態處理工作。我需要看看如何根據我的問題(其中數據綁定正在片段中完成)來適應它。非常感謝! – CommonsWare

+0

@CommonsWare我明白你不想在「相關回復」中進行雙向綁定。所以,我的「解釋」是爲了單向約束。 – Cheticamp

相關問題