115

4.0および4.1タブレット用のアプリを作成しています。このアプリでは、サポートライブラリ(不要な場合)を使用したくありませんが、4.xAPIのみを使用します。

したがって、私のターゲットプラットフォームは次のように非常に明確に定義されています:>=4.0および<=4.1

このアプリには、マルチペインレイアウト(2つのフラグメント、左側に1つ、右側に1つのコンテンツフラグメント)とタブ付きのアクションバーがあります。

これに似ています:

ここに画像の説明を入力してください

アクションバーのタブをクリックすると、「外側」のフラグメントが変更され、内側のフラグメントは2つのネストされたフラグメント(1.小さな左リストフラグメント、2。広いコンテンツフラグメント)を持つフラグメントになります。

フラグメント、特にネストされたフラグメントを置き換えるためのベストプラクティスは何でしょうか。ViewPagerはサポートライブラリの一部であり、このクラスに代わるネイティブの4.xはありません。私の意味では「非推奨」のように見えます。-http ://developer.android.com/reference/android/support/v4/view/ViewPager.html

次に、Android 4.2のリリースノートを読みましたChildFragmentManager。これは適切ですが、4.0と4.1をターゲットにしているため、これも使用できません。

ChildFragmentManager4.2でのみ利用可能です

残念ながら、Android開発者ガイド全体であっても、サポートライブラリなしでフラグメントを使用するためのベストプラクティスを示す良い例はほとんどありません。特にネストされたフラグメントに関しては何もありません。

だから私は疑問に思っています:サポートライブラリとそれに付属するすべてのものを使用せずに、ネストされたフラグメントを使用して4.1アプリを作成することは単純に不可能ですか?(Fragmentなどの代わりにFragmentActivityを使用する必要がありますか?) または、ベストプラクティスは何でしょうか?


私が現在開発中に抱えている問題は、まさにこの声明です。

Androidサポートライブラリもネストされたフラグメントをサポートするようになったため、Android1.6以降でネストされたフラグメントのデザインを実装できます。

注:レイアウトに。が含まれている場合、レイアウトをフラグメントに膨らませることはできません<fragment>。ネストされたフラグメントは、フラグメントに動的に追加された場合にのみサポートされます。

ネストされたフラグメントをXMLで定義するため、次のようなエラーが発生するようです。

Caused by: java.lang.IllegalArgumentException: Binary XML file line #15: Duplicate id 0x7f090009, tag frgCustomerList, or parent id 0x7f090008 with another fragment for de.xyz.is.android.fragment.CustomerListFragment_

現時点では、私は自分自身で結論を下しています。4.1でも、2.xプラットフォームをターゲットにしたくない場合は、スクリーンショットに示すようなネストされたフラグメントは、サポートライブラリなしでは不可能です。

(これは実際には質問というよりもウィキのエントリである可能性がありますが、他の誰かが以前に管理したことがあるかもしれません)。

アップデート:

役立つ答えは次のとおりです。フラグメント内部フラグメント

4

5 に答える 5

61

制限事項

したがって、使用しているバージョンに関係なく、xmlでは別のフラグメント内にフラグメントをネストすることはできませんFragmentManager

したがって、コードを介してフラグメントを追加する必要があります。これは問題のように思われるかもしれませんが、長期的にはレイアウトが非常に柔軟になります。

では、使用せずにネストしgetChildFragmentMangerますか?背後にある本質childFragmentManagerは、前のフラグメントトランザクションが終了するまでロードを延期することです。そしてもちろん、4.2またはサポートライブラリでのみ自然にサポートされていました。

ChildManagerなしのネスト-ソリューション

解決策、もちろんです!私はこれを長い間やっています(ViewPager発表されてから)。

下記参照; これはFragmentロードを延期するため、Fragmentsを内部にロードできます。

非常にシンプルで、Handler非常に便利なクラスです。現在のフラグメントトランザクションのコミットが終了した後、ハンドラーはメインスレッドで実行されるスペースを効果的に待機します(フラグメントはメインスレッドで実行されるUIに干渉するため)。

// Remember this is an example, you will need to modify to work with your code
private final Handler handler = new Handler();
private Runnable runPager;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    return inflater.inflate(R.layout.frag_layout, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);
    runPager = new Runnable() {

        @Override
        public void run()
        {
          getFragmentManager().beginTransaction().addFragment(R.id.frag_container, MyFragment.newInstance()).commit();
        }
    };
    handler.post(runPager);
}

/**
 * @see android.support.v4.app.Fragment#onPause()
 */
@Override
public void onPause()
{
    super.onPause();
    handler.removeCallbacks(runPager);
}

「ベストプラクティス」とは思いませんが、このハックを使用したライブアプリがあり、まだ問題はありません。

ビューページャーの埋め込みにもこの方法を使用します-https ://gist.github.com/chrisjenx/3405429

于 2013-06-16T10:21:07.103 に答える
2

API 17より前でこれを行う最良の方法は、まったく行わないことです。この動作を実装しようとすると、問題が発生します。ただし、それは、現在のAPI14を使用して説得力のある偽造ができないということではありません。私がしたことは次のとおりです。

1-フラグメント間の通信を確認しますhttp://developer.android.com/training/basics/fragments/communicating.html

2-レイアウトxmlFrameLayoutを既存のフラグメントからアクティビティレイアウトに移動し、高さ0を指定して非表示にします。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">
<FrameLayout android:id="@+id/content"
          android:layout_width="300dp"
          android:layout_height="match_parent" />


<FrameLayout android:id="@+id/lstResults"
             android:layout_width="300dp"
             android:layout_height="0dp"
             android:layout_below="@+id/content"
             tools:layout="@layout/treeview_list_content"/>


<FrameLayout android:id="@+id/anomalies_fragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
        android:layout_toRightOf="@+id/content" />

3-親フラグメントにインターフェースを実装します

    OnListener mCallback;

// Container Activity must implement this interface
public interface OnListener 
{
    public void onDoSomethingToInitChildFrame(/*parameters*/);
    public void showResults();
    public void hideResults();
}

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

    // This makes sure that the container activity has implemented
    // the callback interface. If not, it throws an exception
    try {
        mCallback = (OnFilterAppliedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnListener");
    }
}

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

    mCallback.showResults();
}

@Override
public void onPause()
{
    super.onPause();

    mCallback.hideResults();
}

public void onClickButton(View view)
{
    // do click action here

    mCallback.onDoSomethingToInitChildFrame(/*parameters*/);
}

4-親アクティビティにインターフェースを実装します

パブリッククラスYourActivityextendsActivityはyourParentFragment.OnListenerを実装します{

public void onDoSomethingToInitChildFrame(/*parameters*/)
{
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment == null)
    {
        childFragment = new yourChildFragment(/*parameters*/);
        ft.add(R.id.lstResults, childFragment, "Results");
    }
    else
    {
        ft.detach(childFragment);

        ((yourChildFragment)childFragment).ResetContent(/*parameters*/);

        ft.attach(childFragment);
    }
    ft.commit();

    showResultsPane();
}

public void showResults()
{
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment != null)
        ft.attach(childFragment);
    ft.commit();

    showResultsPane();
}

public void showResultsPane()
{
    //resize the elements to show the results pane
    findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
    findViewById(R.id.lstResults).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}

public void hideResults()
{
    //resize the elements to hide the results pane
    findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
    findViewById(R.id.lstResults).getLayoutParams().height = 0;

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment != null)
        ft.detach(childFragment);
    ft.commit();
}

}

5-このメソッドを使用すると、API 17より前の環境でgetChildFragmentManager()関数を使用した場合と同じ流動的な機能を利用できます。子フラグメントが実際には親フラグメントの子ではなく、アクティビティの子になっていることに気付いたかもしれませんが、これは避けられません。

于 2014-09-04T15:13:17.453 に答える
1

私は、NavigationDrawer、TabHost、およびViewPagerの組み合わせが原因で、この正確な問題に対処する必要がありました。これは、TabHostのためにサポートライブラリの使用に問題がありました。また、JellyBean 4.1の最小APIもサポートする必要があったため、getChildFragmentManagerでネストされたフラグメントを使用することはできませんでした。

だから私の問題は蒸留することができます...

TabHost(トップレベル用)
+ ViewPager(トップレベルのタブ付きフラグメントの1つのみ)
=ネストされたフラグメントの必要性(JellyBean 4.1はサポートしません)

私の解決策は、実際にフラグメントをネストせずに、ネストされたフラグメントの錯覚を作成することでした。これを行うには、メインアクティビティでTabHostとViewPagerを使用して、layout_weightを0と1の間で切り替えることで可視性を管理する2つの兄弟ビューを管理します。

//Hide the fragment used by TabHost by setting height and weight to 0
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 0);
mTabHostedView.setLayoutParams(lp);
//Show the fragment used by ViewPager by setting height to 0 but weight to 1
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1);
mPagedView.setLayoutParams(lp);

これにより、関連するレイアウトウェイトを手動で管理している限り、偽の「ネストされたフラグメント」を独立したビューとして効果的に動作させることができました。

これが私のactivity_main.xmlです:

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ringofblades.stackoverflow.app.MainActivity">

    <TabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <FrameLayout android:id="@android:id/tabcontent"
                android:background="@drawable/background_image"
                android:layout_width="match_parent"
                android:layout_weight="0.5"
                android:layout_height="0dp"/>
            <android.support.v4.view.ViewPager
                xmlns:tools="http://schemas.android.com/tools"
                android:id="@+id/pager"
                android:background="@drawable/background_image"
                android:layout_width="match_parent"
                android:layout_weight="0.5"
                android:layout_height="0dp"
                tools:context="com.ringofblades.stackoverflow.app.MainActivity">
                <FrameLayout
                    android:id="@+id/container"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </android.support.v4.view.ViewPager>
            <TabWidget android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </TabHost>

    <fragment android:id="@+id/navigation_drawer"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:name="com.ringofblades.stackoverflow.app.NavigationDrawerFragment"
        tools:layout="@layout/fragment_navigation_drawer" />
</android.support.v4.widget.DrawerLayout>

「@+id/pager」と「@+id / container」は「android:layout_weight = "0.5"」と「android:layout_height="0dp"」の兄弟であることに注意してください。これは、どの画面サイズでもプレビューアで確認できるようにするためです。とにかく、それらの重みは実行時にコードで操作されます。

于 2014-04-03T09:51:57.417 に答える
1

@ Chris.Jenkinsの回答に基づいて、これはライフサイクルイベント(IllegalStateExceptionsをスローする傾向がある)中にフラグメントを削除するために私にとってうまく機能しているソリューションです。これは、HandlerアプローチとActivity.isFinishing()チェックの組み合わせを使用します(そうでない場合、「onSaveInstanceStateの後にこのアクションを実行できません」のエラーがスローされます)。

import android.app.Activity;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

public abstract class BaseFragment extends Fragment {
    private final Handler handler = new Handler();

    /**
     * Removes the {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to
     * compensate for illegal states.
     *
     * @param fragment The {@link Fragment} to schedule for removal.
     */
    protected void removeFragment(@Nullable final Fragment fragment) {
        if (fragment == null) return;

        final Activity activity = getActivity();
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (activity != null && !activity.isFinishing()) {
                    getFragmentManager().beginTransaction()
                            .remove(fragment)
                            .commitAllowingStateLoss();
                }
            }
        });
    }

    /**
     * Removes each {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to
     * compensate for illegal states.
     *
     * @param fragments The {@link Fragment}s to schedule for removal.
     */
    protected void removeFragments(final Fragment... fragments) {
        final FragmentManager fragmentManager = getFragmentManager();
        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        for (Fragment fragment : fragments) {
            if (fragment != null) {
                fragmentTransaction.remove(fragment);
            }
        }

        final Activity activity = getActivity();
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (activity != null && !activity.isFinishing()) {
                    fragmentTransaction.commitAllowingStateLoss();
                }
            }
        });
    }
}

使用法:

class MyFragment extends Fragment {
    @Override
    public void onDestroyView() {
        removeFragments(mFragment1, mFragment2, mFragment3);
        super.onDestroyView();
    }
}
于 2015-08-07T21:39:26.757 に答える
1

OPには、サポートライブラリを使用できない特別な状況がある場合がありますが、ほとんどの人はそれを使用する必要があります。Androidのドキュメントで推奨されており、可能な限り幅広いユーザーがアプリを利用できるようになります。

ここでのより完全な回答では、サポートライブラリでネストされたフラグメントを使用する方法を示す例を作成しました。

ここに画像の説明を入力してください

于 2016-09-13T10:36:05.747 に答える