前文: 私は ActionBarSherlock を使用しています。ターゲット SDK は 17 (Android 4.2)、最小 SDK は 5 (Android 2.0) です。
状況:
アプリにフラグメントがあり、ある種のログインが表示されます。これにより、ユーザーはデバイス上で複数のユーザー アカウントを持つことができます (Android < 4.2 ではシステムがサポートされておらず、ユーザーはデバイス共有のためにそれを使用しているようです)。
フラグメント レイアウトは、リストビューと大きなランドス ケープで構成され、新しいアカウントを追加するためのボタンも含まれます (それ以外の場合は、利用可能なオーバーフロー メニューを使用します)。
リストビューは、「AccountAdapter」と呼ばれる BaseAdapter-Derivate にアタッチされ、データベースからアカウントを取得し、layoutinflater を介してアカウントごとに対応する子ビューを作成します。ユーザーがログインとパスワードの両方を提供する/ユーザーがログインのみを提供する/ユーザーが何も提供しないという 3 つの可能性があります。したがって、不足しているデータが EditText-Elements を介して要求され、格納されたデータが TextView を介して表示される場合には、3 つの異なるレイアウトがあります。また、欠落データのあるレイアウトには、欠落データを保存するためのチェックボックスと送信ボタンがあります。すべてのデータが提供されると、不足している送信ボタンの onclicklistener がアカウントのルート ビューに直接アタッチされます。onclicklistener は、入力されたデータをネットワーク コード クラスにメッセージで送信し、アダプタ モードを変更します。その後、アダプタは選択されたエントリのみを表示します。
これはすでにうまく機能しています。
問題:
向きを変更すると、入力したすべてのデータが失われます。入力されたログイン データ、パスワード、ログイン データを保存するかどうかに関する情報。
レイアウト:
<!-- layout/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="@id/main_fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- layout-large-land/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="@id/main_fragment_sidebar"
android:layout_width="@dimen/main_sidebar_width"
android:layout_height="match_parent" />
<FrameLayout
android:id="@id/main_fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- layout/fragment_login.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@id/fragment_login_accountlist"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</RelativeLayout>
<!-- layout-large-land/fragment_login.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@id/fragment_login_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="@string/string_fragment_login" />
<ListView
android:id="@id/fragment_login_accountlist"
android:layout_width="@dimen/fragment_login_accountlist_width"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
</ListView>
</RelativeLayout>
<!-- layout/view_login_account_new.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@id/view_login_account_profileimage"
android:layout_width="@dimen/view_login_account_profileimage_width"
android:layout_height="@dimen/view_login_account_profileimage_height"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/string_view_login_account_profileimage_contentdescription"
android:scaleType="fitCenter"
android:src="@drawable/img_kb" />
<EditText
android:id="@id/view_login_account_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:ems="10"
android:hint="@string/string_view_login_account_username_hint"
android:inputType="text" >
<requestFocus />
</EditText>
<EditText
android:id="@id/view_login_account_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/view_login_account_username"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_username"
android:ems="10"
android:hint="@string/string_view_login_account_password_hint"
android:inputType="textPassword" />
<CheckBox
android:id="@id/view_login_account_storecredentials"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_password"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:text="@string/string_view_login_account_storecredentials_text" />
<Button
android:id="@id/view_login_account_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_storecredentials"
android:text="@string/string_view_login_account_submit_text" />
</RelativeLayout>
<!-- layout/view_login_account_progress.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@id/view_login_account_profileimage"
android:layout_width="@dimen/view_login_account_profileimage_width"
android:layout_height="@dimen/view_login_account_profileimage_height"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/string_view_login_account_profileimage_contentdescription"
android:scaleType="fitCenter"
android:src="@drawable/img_kb" />
<TextView
android:id="@id/view_login_account_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:ems="10"
>
</TextView>
<TextView
android:id="@id/view_login_account_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/view_login_account_username"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_username"
android:ems="10"
android:text="@string/string_view_login_account_progress_text" />
</RelativeLayout>
<!-- layout/view_login_account_stored_password.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@id/view_login_account_profileimage"
android:layout_width="@dimen/view_login_account_profileimage_width"
android:layout_height="@dimen/view_login_account_profileimage_height"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/string_view_login_account_profileimage_contentdescription"
android:scaleType="fitCenter"
android:src="@drawable/img_kb" />
<TextView
android:id="@id/view_login_account_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:ems="10"
>
</TextView>
<TextView
android:id="@id/view_login_account_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/view_login_account_username"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_username"
android:ems="10"
android:text="@string/string_view_login_account_password_text"
/>
</RelativeLayout>
<!-- layout/view_login_account_stored_username.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@id/view_login_account_profileimage"
android:layout_width="@dimen/view_login_account_profileimage_width"
android:layout_height="@dimen/view_login_account_profileimage_height"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/string_view_login_account_profileimage_contentdescription"
android:scaleType="fitCenter"
android:src="@drawable/img_kb" />
<TextView
android:id="@id/view_login_account_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:ems="10"
android:hint="@string/string_view_login_account_username_hint" >
<requestFocus />
</TextView>
<EditText
android:id="@id/view_login_account_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/view_login_account_username"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_username"
android:ems="10"
android:hint="@string/string_view_login_account_password_hint"
android:inputType="textPassword" />
<CheckBox
android:id="@id/view_login_account_storecredentials"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_password"
android:layout_toRightOf="@id/view_login_account_profileimage"
android:text="@string/string_view_login_account_storecredentials_text" />
<Button
android:id="@id/view_login_account_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/view_login_account_storecredentials"
android:text="@string/string_view_login_account_submit_text" />
</RelativeLayout>
コード:
コードは両方ともまだ onSaveInstanceState と onConfigurationChange を実装していません。(全体の引用は長すぎます。1.000行以上のコードです)
public class MessengerActivity extends SherlockFragmentActivity {
// -------------------------------------------------------------------------
FrameLayout fragmentSidebar = null;
FrameLayout fragmentContent = null;
Content content = null;
// -------------------------------------------------------------------------
boolean hasSidebar = false;
// -------------------------------------------------------------------------
public void onCreate(Bundle savedInstanceState) {
// ---------------------------------------------------------------------
super.onCreate(savedInstanceState);
// ---------------------------------------------------------------------
content = Content.getInstance(this);
// ---------------------------------------------------------------------
setContentView(R.layout.main);
// ---------------------------------------------------------------------
captureFragmentViews();
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
if (savedInstanceState == null) {
// -----------------------------------------------------------------
gotoLogin();
// -----------------------------------------------------------------
}
else {
// -----------------------------------------------------------------
// -----------------------------------------------------------------
}
// ---------------------------------------------------------------------
}
// -------------------------------------------------------------------------
// Disabled through android manifest at the moment
@Override
public void onConfigurationChanged(Configuration newConfig) {
// ---------------------------------------------------------------------
super.onConfigurationChanged(newConfig);
// ---------------------------------------------------------------------
Log.d(getClass().getSimpleName(), "onConfigurationChanged");
// ---------------------------------------------------------------------
}
// -------------------------------------------------------------------------
public void captureFragmentViews() {
// ---------------------------------------------------------------------
fragmentSidebar = (FrameLayout) findViewById(R.id.main_fragment_sidebar);
fragmentContent = (FrameLayout) findViewById(R.id.main_fragment_content);
// ---------------------------------------------------------------------
if ((fragmentSidebar != null) && (fragmentContent != null)) {
hasSidebar = true;
}
else {
hasSidebar = false;
}
// ---------------------------------------------------------------------
}
// -------------------------------------------------------------------------
public void gotoLogin() {
// ---------------------------------------------------------------------
Fragment fragment = SherlockFragment.instantiate(this,
LoginFragment.class.getName());
// ---------------------------------------------------------------------
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (hasSidebar) {
ft.add(R.id.main_fragment_content, fragment);
fragmentSidebar.setVisibility(View.GONE);
}
else {
ft.add(R.id.main_fragment_content, fragment);
}
// ---------------------------------------------------------------------
ft.commit();
getSupportFragmentManager().executePendingTransactions();
// ---------------------------------------------------------------------
}
// -------------------------------------------------------------------------
public void gotoSignup() {
// TODO Auto-generated method stub
}
// -------------------------------------------------------------------------
public void gotoContactList(int filterId) {
// TODO Auto-generated method stub
}
// -------------------------------------------------------------------------
public void gotoConversation(int userId) {
// TODO Auto-generated method stub
}
// -------------------------------------------------------------------------
public void gotoOnlineStatusList(int categoryId) {
// TODO Auto-generated method stub
}
// -------------------------------------------------------------------------
public void gotoSettings(int categoryId) {
// TODO Auto-generated method stub
}
// -------------------------------------------------------------------------
}
メイン アクティビティで savedInstanceState が設定されているかどうかを確認するため、アクティビティはフラグメントの再作成に関して既に「準備」されています。
フラグメントは、アダプターを作成してリストビューにアタッチする前に、アダプターが既に存在するかどうかを確認します。しかし:私の理解では、「再作成された」フラグメントが同じアダプターを使用していても、「デフォルトで」すべての getViews() がリコールされるため、「リセットされた」コンテンツを持つビューの新しいインスタンスが作成されます。
AccountAdapter は、データベースからユーザー ID、ユーザー名、パスワードを持つ専用の「アカウント」オブジェクトを取得します。また、「アカウント」を拡張して、ビューをキャッシュするなど、追加のデータを保持することもできます。
私の考えは、次のような getView を実装することでした:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// ---------------------------------------------------------------------
final Account account = (Account) getItem(position);
View view = null;
// ---------------------------------------------------------------------
if (account == null) {
return view;
}
// ---------------------------------------------------------------------
if (account.view != null) {
return account.view;
}
// ---------------------------------------------------------------------
[...]
}
しかし、その後、AccountAdapter が再現されなかったことに気付きました。Android は LoginFragment クラスを再インスタンス化します。
必要なもの:
- リストビュー コンテンツ フォーミュラーからデータを復元する方法 / AccountAdapter とそれに関連付けられたビューを保持する方法
- 建築設計を最適化および改善するためのヒント