android - 坚持从 Activity 移植到 fragment

标签 android android-activity fragment

我尝试将一个简单的基于 Activity 的应用程序移植到 fragment 中已经整整一周了。我完全卡住了。

这个野兽是一个简单的列表、详细信息、添加/编辑应用程序,带有上下文菜单和选项菜单。我试图做到正确: fragment 和 Activity 各自在自己的文件中,使用手机和平板电脑的 v4 支持包, fragment 完成了可重用 fragment 应该做的一切,回调(很多)飞来飞去通知 Activity 和关于该做什么的 fragment 。从 SQLiteOpenHelper 转换为 ContentProvider,从 optionmenu 转换为 actionbarmenu,以及,和,以及,……(我使用的几乎所有内容现在都已弃用)。

太可怕了。我的基于 Activity 的简单小型工作应用程序现在的大小几乎是原来的 3 倍,而且很多东西还不能正常工作。

如果需要,我可以在此处添加我的代码 - 但它有很多东西(您已被警告)。

我的问题:是否有人愿意分享一个包含列表、详细信息添加/编辑的完整示例?此示例应为 Fragments 和 Activity 使用单独的文件(而不是来自 Google 的一体化包)。

请不要投反对票。我真的很想看看如何使它正确。

非常感谢。

编辑:

这是开始 Activity ,它有两种布局(res/layout 用于手机,res/layout-large-land 用于平板电脑)和上下文菜单:

public class ActivityList extends FragmentActivity implements FragmentList.MyContextItemSelectedListener,
                                                      FragmentList.MyDeleteListener,
                                                      FragmentList.MyListItemClickListener,
                                                      FragmentList.MyOptionsItemSelectedListener,
                                                      FragmentDetails.MyDeleteListener,
                                                      FragmentDetails.MyOptionsItemSelectedListener {

    @Override
    public void myContextItemSelected(final int action, final long id) {
        if (action == R.id.men_add) {
            processEdit(0);
        } else if (action == R.id.men_delete) {
            processUpdateList();
        } else if (action == R.id.men_details) {
            processDetails(id);
        } else if (action == R.id.men_edit) {
            processEdit(id);
        }
    }

    @Override
    public void myDelete(final long id) {
        processUpdateList();
    }

    @Override
    public void myListItemClick(final long id) {
        processDetails(id);
    }

    @Override
    public void myOptionsItemSelected(final int action) {
        myOptionsItemSelected(action, 0);
    }

    @Override
    public void myOptionsItemSelected(final int action, final long id) {
        if (action == R.id.men_add) {
            processEdit(0);
        } else if (action == R.id.men_edit) {
            processEdit(id);
        } else if (action == R.id.men_preferences) {
            processPreferences();
        }
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
        processUpdateList();
    }

    @Override
    public void onCreate(final Bundle bundle) {
        super.onCreate(bundle);

        setContentView(R.layout.activitylist);
    }

    private void processEdit(final long id) {
        Intent intent = new Intent(this, ActivityEdit.class);
        intent.putExtra("ID", id);
        startActivityForResult(intent, MyConstants.DLG_TABLE1EDIT);
    }

    private void processDetails(final long id) {
        if (Tools.isXlargeLand(getApplicationContext())) {
            Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.right);
            if (fragment == null ||
                    (fragment instanceof FragmentDetails && ((FragmentDetails) fragment).getCurrentId() != id)) {
                fragment = new FragmentDetails(id);

                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                transaction.replace(R.id.right, fragment);
                transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                transaction.commit();
            }
        } else {
            Intent intent = new Intent(this, ActivityDetails.class);
            intent.putExtra("ID", id);
            startActivityForResult(intent, MyConstants.DLG_TABLE1SHOW);
        }
    }

    private void processPreferences() {
        Intent intent = new Intent(this, MyPreferenceActivity.class);
        startActivityForResult(intent, MyConstants.DLG_PREFERENCES);
    }

    private void processUpdateList() {
        // TODO:
    }
}

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <fragment 
        class="com.test.app.FragmentList"
        android:id="@+id/fragmentlist"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:name="com.test.app.FragmentList" />
</LinearLayout>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <fragment 
        class="com.test.app.FragmentList"
        android:id="@+id/fragmentlist"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:layout_width="0dip"
        android:name="com.test.app.FragmentList" />

    <FrameLayout
        android:id="@+id/right"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:layout_width="0dip" />
</LinearLayout>

这是带有行布局、选项菜单和上下文菜单的 ListFragment:

public class FragmentList extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

    private SimpleCursorAdapter           adapter;
    private AlertDialog                   alertDialog;
    private Context                       context;
    private MyContextItemSelectedListener contextItemSelectedListener;
    private MyDeleteListener              deleteListener;
    private long                          id;
    private MyListItemClickListener       listItemClickListener;
    private ListView                      listView;
    private MyOptionsItemSelectedListener optionsItemSelectedListener;

    public interface MyContextItemSelectedListener {
        public void myContextItemSelected(int action, long id);
    }

    public interface MyDeleteListener {
        public void myDelete(long id);
    }

    public interface MyListItemClickListener {
        public void myListItemClick(long id);
    }

    public interface MyOptionsItemSelectedListener {
        public void myOptionsItemSelected(int action);
    }

    @Override
    public void onActivityCreated(final Bundle bundle) {
        super.onActivityCreated(bundle);

        context = getActivity().getApplicationContext();

        listView = getListView();

        getActivity().getSupportLoaderManager().initLoader(MyConstants.LDR_TABLE1LIST, null, this);

        adapter = new SimpleCursorAdapter(context,
                                          R.layout.fragmentlist_row,
                                          null,
                                          new String[] { Table1.DESCRIPTION },
                                          new int[] { R.id.fragmentlist_row_description },
                                          CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        setListAdapter(adapter);
        setListShown(false);

        registerForContextMenu(listView);

        if (bundle != null && bundle.containsKey("ID")) {
            id = bundle.getLong("ID");
            listItemClickListener.myListItemClick(id);
        }

        if (Tools.isXlargeLand(context)) {
            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        }

        setHasOptionsMenu(true);
    }

    @Override
    public void onAttach(final Activity activity) {
        super.onAttach(activity);

        // Reduced: Check for implemented listeners
    }

    @Override
    public boolean onContextItemSelected(final MenuItem menuItem) {
        AdapterContextMenuInfo adapterContextMenuInfo = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();

        final long id = adapterContextMenuInfo.id;

        if (menuItem.getItemId() == R.id.men_delete) {
            processAlertDialog(id);
            return true;
        } else {
            contextItemSelectedListener.myContextItemSelected(menuItem.getItemId(), adapterContextMenuInfo.id);
        }

        return super.onContextItemSelected(menuItem);
    }

    @Override
    public void onCreateContextMenu(final ContextMenu contextMenu, final View view, final ContextMenuInfo contextMenuInfo) {
        super.onCreateContextMenu(contextMenu, view, contextMenuInfo);

        if (view.getId() == android.R.id.list) {
            getActivity().getMenuInflater().inflate(R.menu.fragmentlist_context, contextMenu);
        }
    }

    @Override
    public Loader<Cursor> onCreateLoader(final int id, final Bundle bundle) {
        MyCursorLoader loader = null;

        switch (id) {
            case MyConstants.LDR_TABLE1LIST:
                loader = new MyCursorLoader(context,
                                            MySQLiteOpenHelper.TABLE1_FETCH,
                                            null);
                break;
        }

        return loader;
    }

    @Override
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater menuInflater) {
        super.onCreateOptionsMenu(menu, menuInflater);

        menu.clear();

        menuInflater.inflate(R.menu.fragmentlist, menu);
    }

    @Override
    public void onListItemClick(final ListView listView, final View view, final int position, final long id) {
        super.onListItemClick(listView, view, position, id);

        this.id = id;

        if (Tools.isXlargeLand(context)) {
            listView.setItemChecked(position, true);
        }

        listItemClickListener.myListItemClick(id);
    }

    @Override
    public void onLoaderReset(final Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }

    @Override
    public void onLoadFinished(final Loader<Cursor> loader, final Cursor cursor) {
        adapter.swapCursor(cursor);

        setListShown(true);
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem menuItem) {
        optionsItemSelectedListener.myOptionsItemSelected(menuItem.getItemId());

        return super.onOptionsItemSelected(menuItem);
    }

    @Override
    public void onSaveInstanceState(final Bundle bundle) {
        super.onSaveInstanceState(bundle);

        bundle.putLong("ID", id);
    }

    private void processAlertDialog(final long id) {
        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
        alertDialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(final DialogInterface dialogInterface, final int which) {
                dialogInterface.dismiss();
            }
        } );
        alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(final DialogInterface dialogInterface, final int which) {
                MyApplication.getSqliteOpenHelper().deleteTable1(id);

                alertDialog.dismiss();

                deleteListener.myDelete(id);
            }
        } );
        alertDialogBuilder.setCancelable(false);
        alertDialogBuilder.setMessage(R.string.txt_reallydelete);

        alertDialog = alertDialogBuilder.create();
        alertDialog.show();
    }
}

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:orientation="horizontal"
    android:paddingBottom="2dip"
    android:paddingTop="2dip" >

    <TextView
        style="@style/TextViewLarge"
        android:id="@+id/fragmentlist_row_description"
        android:textStyle="bold" />
</LinearLayout>


<menu
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:icon="@drawable/ic_menu_add"
        android:id="@+id/men_add"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_add" />

    <item
        android:icon="@drawable/ic_menu_preferences"
        android:id="@+id/men_preferences"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_preferences" />
</menu>

<menu
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/men_details"
        android:title="@string/txt_details" />

    <item
        android:id="@+id/men_edit"
        android:title="@string/txt_edit" />

    <item
        android:id="@+id/men_delete"
        android:title="@string/txt_delete" />
</menu>

这是 DetailsActivity:

public class ActivityDetails extends FragmentActivity implements FragmentDetails.MyDeleteListener, 
                                                                    FragmentDetails.MyOptionsItemSelectedListener {

    private long id;

    @Override
    public void myDelete(final long id) {
        setResult(RESULT_OK);
        finish();
    }

    @Override
    public void myOptionsItemSelected(final int action, final long id) {
        if (action == R.id.men_add) {
            processEdit(0);
        } else if (action == R.id.men_edit) {
            processEdit(id);
        } else if (action == R.id.men_preferences) {
            processPreferences();
        }
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
        if (requestCode == MyConstants.DLG_PREFERENCES || requestCode == MyConstants.DLG_TABLE1EDIT) {
            finish();

            startActivity(getIntent()); 
        }
    }

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

        if (bundle != null) {
            if (bundle.containsKey("ID")) {
                id = bundle.getLong("ID");
            }
        } else {
            Bundle bundleExtras = getIntent().getExtras();
            if (bundleExtras != null) {
                id = bundleExtras.getLong("ID");
            }

            processDetails(id);
        }
    }

    @Override
    public void onSaveInstanceState(final Bundle bundle) {
        super.onSaveInstanceState(bundle);

        bundle.putLong("ID", id);
    }

    private void processDetails(final long id) {
        FragmentDetails fragment = new FragmentDetails(id);

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(android.R.id.content, fragment);
        transaction.commit();
    }

    private void processEdit(final long id) {
        Intent intent = new Intent(this, ActivityEdit.class);
        intent.putExtra("ID", id);
        startActivityForResult(intent, MyConstants.DLG_TABLE1EDIT);
    }

    private void processPreferences() {
        Intent intent = new Intent(this, MyPreferenceActivity.class);
        startActivityForResult(intent, MyConstants.DLG_PREFERENCES);
    }
}

这是带有布局和菜单的 DetailsFragment:

public class FragmentDetails extends Fragment {

    private AlertDialog                   alertDialog;
    private MyDeleteListener              deleteListener;
    private long                          id;
    private MyOptionsItemSelectedListener optionsItemSelectedListener;
    private TextView                      textViewDescription;
    private TextView                      textViewId;

    public FragmentDetails() {
        id = 0;
    }

    public FragmentDetails(final long id) {
        this.id = id;
    }

    public long getCurrentId() {
        return id;
    }

    public interface MyDeleteListener {
        public void myDelete(long id);
    }

    public interface MyOptionsItemSelectedListener {
        public void myOptionsItemSelected(int action, long id);
    }

    @Override
    public void onActivityCreated(final Bundle bundle) {
        super.onActivityCreated(bundle);

        if (bundle != null && bundle.containsKey("ID")) {
            id = bundle.getLong("ID");
        }

        setHasOptionsMenu(true);
    }

    @Override
    public void onAttach(final Activity activity) {
        super.onAttach(activity);

        // Reduced
    }

    @Override
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater menuInflater) {
        super.onCreateOptionsMenu(menu, menuInflater);

        menu.clear();

        menuInflater.inflate(R.menu.fragmentdetails, menu);
    }

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup viewGroup, final Bundle bundle) {
        View view = inflater.inflate(R.layout.fragmentdetails, null);

        textViewDescription = (TextView) view.findViewById(R.id.tv_description);
        textViewId = (TextView) view.findViewById(R.id.tv_id);

        if (id != 0) {
            Table1 table1;
            if ((table1 = MyApplication.getSqliteOpenHelper().getTable1(id)) != null) {
                textViewDescription.setText(Tools.defaultString(table1.getDescription()));
                textViewId.setText(Tools.defaultString(String.valueOf(table1.getId())));
            }
        }

        return view;
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem menuItem) {
        if (menuItem.getItemId() == R.id.men_delete) {
            processAlertDialog(id);
            return true;
        } else {
            optionsItemSelectedListener.myOptionsItemSelected(menuItem.getItemId(), id);
        }

        return super.onOptionsItemSelected(menuItem);
    }

    @Override
    public void onSaveInstanceState(final Bundle bundle) {
        super.onSaveInstanceState(bundle);

        bundle.putLong("ID", id);
    }

    private void processAlertDialog(final long id) {
        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
        alertDialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(final DialogInterface dialogInterface, final int which) {
                alertDialog.dismiss();
                alertDialog = null;
            }
        } );
        alertDialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(final DialogInterface dialogInterface, final int which) {
                MyApplication.getSqliteOpenHelper().deleteTable1(id);

                alertDialog.dismiss();
                alertDialog = null;

                deleteListener.myDelete(id);
            }
        } );
        alertDialogBuilder.setCancelable(false);
        alertDialogBuilder.setMessage(R.string.txt_reallydelete);

        alertDialog = alertDialogBuilder.create();
        alertDialog.show();
    }
}

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:orientation="horizontal" >

        <TextView
            style="@style/TextViewStandard"
            android:layout_weight="1" 
            android:text="@string/txt_id" />

        <TextView
            style="@style/TextViewStandard"
            android:id="@+id/tv_id"
            android:layout_weight="1" />
    </LinearLayout>

    <LinearLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:orientation="horizontal" >

        <TextView
            style="@style/TextViewStandard"
            android:layout_weight="1" 
            android:text="@string/txt_description" />

        <TextView
            style="@style/TextViewStandard"
            android:id="@+id/tv_description"
            android:layout_weight="1" />
    </LinearLayout>
</LinearLayout>

<menu
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:icon="@drawable/ic_menu_add"
        android:id="@+id/men_add"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_add" />

    <item
        android:icon="@drawable/ic_menu_edit"
        android:id="@+id/men_edit"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_edit" />

    <item
        android:icon="@drawable/ic_menu_delete"
        android:id="@+id/men_delete"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_delete" />

    <item
        android:icon="@drawable/ic_menu_preferences"
        android:id="@+id/men_preferences"
        android:showAsAction="ifRoom|withText"
        android:title="@string/txt_preferences" />
</menu>

我没有发布 EditActivity,因为它只是一个没有 Fragment 的 FragmentActivity。

最佳答案

这可能不是全部答案,而是部分答案: 您仍然有一个主要 Activity ,在您曾经有 ListView 的 xml 中,您现在添加了一个框架布局。然后在您的 oncreate Activity 中添加以下内容:

        mMainFragment = new ListFragment();
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        fragmentTransaction.replace(R.id.center_container, mMainFragment);

        fragmentTransaction.commit();
        mCurrentFragment = mMainFragment;

在你的列表 fragment 中

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // setup view
    View view = inflater.inflate(R.layout.calendar_list, null);

    mListAdapter = new CustomAdapter(getActivity(), R.layout.calendar_row, (ArrayList<Item>) mFullList);
    setListAdapter(mListAdapter);

    return view;
}

列表 fragment 的 XML:

<somelayout>
    <ListView android:id="@id/android:list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</somelayout>

点击列表在 fragment 中触发:

@Override
public void onListItemClick(ListView list, View view, int position, long id) {
    final Item item = (Item) list.getAdapter().getItem(position);
    mListener.OnListClick(item);
}

它使用这个监听器:

公共(public)接口(interface) OnListItemClickListener { public void OnListClick(Item item);

Listfragment 需要将其置于顶部:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnListItemClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnListItemClickListener");
    }
}

然后主 Activity 通过实现接口(interface)订阅此内容,并在触发监听器时启动详细信息 fragment 。

编辑: 好的,所以你的问题要基本得多:)记住 oncreate 每次旋转时都会在你的 Activity 中调用,所以你的 Activity 需要记住要显示哪个 fragment ,就像它需要记住要显示哪个 View 一样。 您还需要将 fragment 添加到返回堆栈,否则返回键将无法使用它们。将 fragment 视为具有功能的 View ,它们不是 Activity 。

关于android - 坚持从 Activity 移植到 fragment ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10013023/

相关文章:

android - fragment 尚未附加

php - INSERT Row If Does Not Exist mysql with POST 插入行

android - 应用程序未显示在全局快速搜索框中

android - 有没有android :largeHeap ="true"的替代品

javascript - 我是否需要在 cordova/phonegap 项目的所有 html 文件中添加 app.initialize()

android - 致命信号 11 (SIGSEGV),代码 1,故障地址 0x48 in tid 21741 (RenderThread) in android in rear case in some device

java - Android Studio Activity之间切换变量

Android Status Bar Notifications - 选择通知时打开正确的 Activity

Android 在 vi​​ewpager 中点击转到第一个 fragment 后返回同一个 fragment

java - 在android中浏览Fragment而不将其添加到backstack中