安卓碎片显示为对话框碎片或普通碎片

15
我试图实现的目标是,在平板上显示为DialogFragment,而在智能手机上则作为普通片段显示。我知道已经有一个类似的帖子,但我无法使其工作 - 将样式应用于片段。
为了从上到下显示事物,MainActivity.java:
public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_next) {
            decideToNext();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void decideToNext() {
        String device = getString(R.string.device);
        if ("normal".equalsIgnoreCase(device)) {
            Intent intent = new Intent(this, DetailedActivity.class);
            startActivity(intent);
        } else if ("large".equalsIgnoreCase(device)) {
            Log.d("SOME_TAG", "Yes, I am seeing this line on tablet only");
            DetailedFragment fragment = DetailedFragment.newInstance();
            getSupportFragmentManager().beginTransaction().add(fragment, "MAGIC_TAG").commit();
        }
    }

}

DetailedActivity 并不是什么很重要的东西:

public class DetailedActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.detailed_activity);
    }
}

它的布局:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_container"
    android:name="com.myapps.sampleandroid.DetailedFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

还有一个有趣的DetailedFragment

public class DetailedFragment extends Fragment {

    public static DetailedFragment newInstance() {
        return new DetailedFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.MyDialogTheme);
        LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper);
        return localInflater.inflate(R.layout.detailed_fragment, container, false);
    }
}

...以及它的布局:

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

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Regular Text" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button Dummy" />

</LinearLayout>

onCreateView 中,我尝试设置自定义样式,但似乎在平板电脑上没有起作用。

样式

res/values/styles.xml 包含:

<resources>

    <style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
    </style>

    <style name="MyDialogTheme" />

</resources>

在 res/values-large/styles.xml 中:

<resources>
    <!-- Is there anything I should add here? -->
    <style name="MyDialogTheme" parent="@android:style/Theme.Dialog"/>
</resources>

我已经将MyDialogThemeTheme.Dialog中嵌套,但似乎并没有帮助。

当在智能手机上点击“下一步”操作栏菜单项时,我看到了详细的活动内容(来自SG2的快照):snapshot from an SG2

而当我从平板电脑上点击相同的“下一步”菜单项时,除了查看Logcat上的消息:Yes, I am seeing this line之外,什么也没发生。 enter image description here

为了在平板电脑上将DetailedFragment作为对话框显示,我应该在styles.xml或代码中添加什么内容?

编辑

我尝试了Little Child提出的解决方案(创建一个DialogFragment来包含我的初始片段并显示它)。 因此,我添加了一个WrapperDetailedFragment

public class WraperDetailedFragment extends DialogFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.wrap_detailed_fragment, container, false);
    }
}

它的布局:

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

    <fragment
        android:id="@+id/wrapped_fragment_id"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.myapps.sampleandroid.DetailedFragment" />

</LinearLayout>

MainActivity的代码改变为:

private void decideToNext() {
    String device = getString(R.string.device);
    if ("normal".equalsIgnoreCase(device)) {
        Intent intent = new Intent(this, DetailedActivity.class);
        startActivity(intent);
    } else if ("large".equalsIgnoreCase(device)) {
        Log.d("SOME_TAG", "Yes, I am seeing this line ...");
        WraperDetailedFragment fragment = new WraperDetailedFragment();
        fragment.show(getSupportFragmentManager(), "MAGICAL_TAG");
    }
}

但是当我尝试添加此DialogFragment时,出现以下崩溃:

android.view.InflateException: Binary XML file line #8: Error inflating class fragment
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    at com.myapps.sampleandroid.WraperDetailedFragment.onCreateView(WraperDetailedFragment.java:12)
    at android.support.v4.app.Fragment.performCreateView(Fragment.java:1478)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1082)
    at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:304)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
    at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
    at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
    at com.myapps.sampleandroid.WraperDetailedFragment.onCreateView(WraperDetailedFragment.java:12)
    at android.support.v4.app.Fragment.performCreateView(Fragment.java:1478)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4745)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Binary XML file line #8: Duplicate id 0x7f050047, tag null, or parent id 0x0 with another fragment for com.myapps.sampleandroid.DetailedFragment
    at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:290)
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
    ... 28 more
3个回答

27
如果不是试图使 Fragment 看起来像 DialogFragment,而是从相反的角度考虑,让 DialogFragment 看起来像 Fragment,这个问题就可以很容易地解决 - 毕竟,DialogFragment 就是一个 Fragment
解决方案的关键在于是否调用 DialogFragment.setShowsDialog();
所以将 DetailedFragment 更改为:
public class DetailedFragment extends DialogFragment {

    private static final String ARG_SHOW_AS_DIALOG = "DetailedFragment.ARG_SHOW_AS_DIALOG";

    public static DetailedFragment newInstance(boolean showAsDialog) {
        DetailedFragment fragment = new DetailedFragment();
        Bundle args = new Bundle();
        args.putBoolean(ARG_SHOW_AS_DIALOG, showAsDialog);
        fragment.setArguments(args);
        return fragment;
    }

    public static DetailedFragment newInstance() {
        return newInstance(true);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        if (args != null) {
            setShowsDialog(args.getBoolean(ARG_SHOW_AS_DIALOG, true));
        }
    }

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

页面布局不变,DetailedActivity 更改为:

public class DetailedActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.detailed_activity);
        if (savedInstanceState == null) {
            DetailedFragment fragment = DetailedFragment.newInstance(false);
            getSupportFragmentManager().beginTransaction().add(R.id.root_layout_details, fragment, "Some_tag").commit();
        }
    }
}

同时也要考虑其布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_layout_details"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

而调用方活动只需执行以下操作:

private void decideToNext() {
    String device = getString(R.string.device);
    if ("normal".equalsIgnoreCase(device)) {
        Intent intent = new Intent(this, DetailedActivity.class);
        startActivity(intent);
    } else if ("large".equalsIgnoreCase(device)) {
        DetailedFragment fragment = DetailedFragment.newInstance();
        fragment.show(getSupportFragmentManager(), "Tablet_specific");
    }
}

1
要将 DialogFragment 显示为常规的 Fragment,请使用片段容器的资源 ID 调用 add()replace(),例如:
beginTransaction().add(R.id.fragment_container, fragment)

然而,要使DialogFragment显示为对话框,请调用add(fragment, "Fragment Tag")。在幕后,这导致调用add(resId, fragment),将片段容器的资源ID设置为0,这会导致DialogFragment将其showAsDialog选项设置为true。

因此,您可以根据需要使用对话框片段作为对话框或常规片段,而无需创建任何特殊逻辑来完成它。


0

好的,我已经做过了。要创建一个DialogFragment,您需要在XML中定义一个布局。比如说,您有一个根元素为LinearLayout的布局。在这个LinearLayout中,您可以添加一个fragment class="....",当您显示DialogFragment时,它将是与平板电脑上并排显示的相同Fragment,现在以DialogFragment的形式显示。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<fragment class="com.example.tqafragments.FeedFragment" android:id="@+id/feedFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/>
</LinearLayout>  

像这样。并在您的DialogFragment中填充它。


1
我认为你没有理解问题:我需要一个片段在平板电脑上显示为DialogFragment,但是对于智能手机,同一片段必须保持为非DialogFragment。你提供的答案展示了如何显示一个片段 - 顺便说一下,如果FeedFragment是一个DialogFragment,我认为你会得到一个IllegalArgumentExceptionDialogFragment不能附加到容器视图。只有在创建根后,才能通过FragmentTransation add/replace方法添加DialogFragment。 - gunar
明天试一下,不管怎么样,你的想法很有意思。+1 - gunar
当然可以。DialogFragment 中可以包含一个 Fragment - An SO User
我尝试了你的解决方案,但是出现了崩溃,一些“IllegalArgumentException”。你可以看一下吗? - gunar
1
标签为空,因为您无法从XML中指定,并且没有重复的ID。父级有一个ID。我将不得不深入Android代码以查看这个错误是关于什么的,但乍一看,这似乎不起作用-至少我无法使其工作。您拥有上面的所有资源(类名和布局)。如果您修改上述内容使其正常工作,即使我的答案似乎更干净,我也会接受您的答案。我喜欢有备选方案... - gunar
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接