2017-10-09 1 views
4

TL : 데이터 바인딩과 함께 사용 된 레이아웃이 EditText이고 android:text에 대한 바인딩 표현식이있는 경우 바인딩 표현식은 명시 적으로 바인딩 평가를 트리거하지 않더라도 저장된 인스턴스 상태 값을 덮어 씁니다. 구성 변경 이전에 사용자가 입력 한 내용이 지워집니다. 이 문제를 어떻게 해결하여 구성 변경시 저장된 인스턴스 상태 값이 사용됩니까?저장된 인스턴스 상태를 사용하기 위해 데이터 바인딩을 얻는 방법은 무엇입니까?

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

그리고 우리는 Model이 참조하는 레이아웃이 있습니다 :


우리는 바보 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> 

참고; 우리는 그걸 약간 알아챌거야.

레이아웃은 동적 조각에 사용된다 : 우리는 바인딩에 Model를 밀어 어디서나 setModel()를 호출하지 않는

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()); 
    } 
} 

참고. MainBinding (위에 표시된 main.xml 레이아웃의 경우)은 레이아웃을 팽창시키는 데 사용됩니다.

이 코드 (FragmentActivityFormFragment으로 설정해야 함)는 저장된 인스턴스 상태를 올바르게 사용합니다. 사용자가 EditText에 무언가를 입력 한 다음 화면을 회전 시키면 새로 작성된 EditText에 입력 된 텍스트가 표시됩니다.

자, android:text에 대한 바인딩 식을 추가 할 레이아웃을 변경할 수 있습니다 : 사용자 유형 EditText에 뭔가가 화면을 회전하면

<?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가 비어 있습니다. 바인딩 표현식은 저장된 인스턴스 상태에서 복원 된 프레임 워크를 덮어 씁니다.

이것은 바인딩에 setModel()을 호출하지 않는다는 사실에도 불구하고 나온 것입니다. 어디에서 setModel()이라는 바인딩을 호출하면 모델의 데이터로 EditText 내용을 대체 할 수 있는지 확인할 수 있습니다. 그러나 나는 그렇게하지 않는다.

정식 기기 (Google Pixel, Android 8.0)와 생태계 기기 (Samsung Galaxy S8, Android 7.1)에서이 동작을 재현 할 수 있습니다.

이것은 상태를 직접 저장하고 어느 시점에서 복원하여 "수동으로"해결할 수 있습니다. 예를 들어, 여러 의견은 양방향 바인딩을 제안했지만 다른 디자인 목표 (예 : 불변 모델 객체)와는 반대 방향으로 실행됩니다. 이것은 데이터 바인딩의 근본적인 제한 인 것처럼 보입니다. 그래서 내가 놓친 뭔가가 저장된 인스턴스 상태를 자동으로 사용하도록 구성 할 수 있기를 바랍니다.

+0

'android : text = "@ {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

관련 문제