在片段中在运行时更改方向时更改布局而不重新创建视图

Posted

技术标签:

【中文标题】在片段中在运行时更改方向时更改布局而不重新创建视图【英文标题】:Change layout while orientation change at runtime in a fragment without recreating the view 【发布时间】:2012-12-29 15:23:28 【问题描述】:

我尝试开发第一个从网络下载图像并在网格视图中显示它们的应用程序。 gridview 是主 Activity 的一个片段。下载过程是使用 onCreate 函数中的 AsyncTask 完成的。为了在更改方向时不会再次下载图像,我在 android Manifest 中设置了android:configChanges="orientation|screenSize"。然后 onCreate 函数只调用一次,一切都很好......除了我必须在横向模式下对 gridview 片段的布局进行一些更改。所以我在 layout/ 文件夹中创建了 2 个布局表:fragment_library.xmlfragment_library_land.xml。为了使这些更改生效,我尝试使用 onConfigurationChanged 函数手动更改库片段的布局。在运行时,程序评估函数并传入好的情况(纵向或横向),但使用的布局仍然是纵向模式的布局:fragment_library.xml ...

public class LibraryFragment extends Fragment 
    public GridView gridview;
    private Boolean isImageAdapterPopulated = false;

    @Override
    public void onCreate(Bundle savedInstanceState)
        super.onCreate(savedInstanceState);
        GetLibraryTask getLibraryTask = new GetLibraryTask(this);
        getLibraryTask.execute(Config.URL + "action=getLibrary");
    

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) 
        if (container == null)
            return null;

        // gridview
        View V = inflater.inflate(R.layout.fragment_library, container, false);
        gridview = (GridView)V.findViewById(R.id.gridview);

        if(this.isImageAdapterPopulated)
            this.setGridAdapter();
        return V;
    

    @Override
    public void onConfigurationChanged(Configuration newConfig)
        super.onConfigurationChanged(newConfig);
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 
            LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            inflater.inflate(R.layout.fragment_library_land, null);
         else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
            LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            inflater.inflate(R.layout.fragment_library, null);
        
    

    public void setGridAdapter()
        this.isImageAdapterPopulated = true;
        gridview.setAdapter(new ImageAdapter(getActivity()));
    

    // ...

fragment_library.xml

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/gridview"
    android:cacheColorHint="@android:color/transparent"
    android:layout_ 
    android:layout_
    android:columnWidth="200dp"
    android:numColumns="auto_fit"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="20dp"
    android:stretchMode="columnWidth"
    android:gravity="bottom"
/>

fragment_library_land.xml

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/gridview"
    android:cacheColorHint="@android:color/transparent"
    android:layout_ 
    android:layout_
    android:columnWidth="400dp"
    android:numColumns="2"
    android:verticalSpacing="50dp"
    android:horizontalSpacing="50dp"
    android:stretchMode="columnWidth"
    android:gravity="bottom"
/>

感谢您的帮助:)

【问题讨论】:

【参考方案1】:

这是不可能的。 Fragment 无法动态更新其布局。但是,您确实还有其他一些选择。

1。不喜欢这个,但你可能有一个 Fragment 的布局,同时具有纵向和横向视图,并显示和隐藏。

fragment_library.xml:

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/gridview_portrait"
    android:cacheColorHint="@android:color/transparent"
    android:layout_ 
    android:layout_
    android:columnWidth="200dp"
    android:numColumns="auto_fit"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="20dp"
    android:stretchMode="columnWidth"
    android:gravity="bottom"
/>
<GridView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/gridview_landscape"
    android:cacheColorHint="@android:color/transparent"
    android:layout_ 
    android:layout_
    android:columnWidth="400dp"
    android:numColumns="2"
    android:verticalSpacing="50dp"
    android:horizontalSpacing="50dp"
    android:stretchMode="columnWidth"
    android:gravity="bottom"
    android:visible="gone"
/>

然后是一些私有成员变量:

private GridView mGridViewPortrait;
private GridView mGridViewLandscape;

然后在onConfigurationChanged(Configuration newConfig):

@Override
        public void onConfigurationChanged(Configuration newConfig) 
            super.onConfigurationChanged(newConfig);

            if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
                 mGridViewPortrait.setVisibility(View.VISIBLE);
                 mGridViewLandscape.setVisibility(View.GONE);
            

            else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) 
                 mGridViewPortrait.setVisibility(View.GONE);
                 mGridViewLandscape.setVisibility(View.VISIBLE);
            
        

几点:请注意,我省略了引用两个 GridView 的代码。我还将您的 GridView 更改为私有,并将名称更改为 mGridView*。私有以保持“数据封装”和“m”,因为它是该类的成员,只是约定。我还更改了 if-else 子句,因为我希望先进行纵向检查。

这种方式是最快和最简单的,但是如果你有很大的布局,它可能会变得很重,所以如果你有很多东西,不要使用这个。最好不要使用这种方法。

2。正确的方法是让 Andorid 处理方向并将您的 XML 移动到正确的目录。但是,这将重新创建您的 Fragment(如果您没有设置 setRetainInstance(true);,在这种情况下您将不会这样做;这将使 Fragment 不会重新创建它的布局(实际上查找 the retain method 它没有提到 onCreateView 所以您可以尝试将此设置为 true 并尝试))。

将您的 fragment_library_land.xml 移动到目录 layout-land 而不是 layout 并将其命名为 fragment_library.xml。注意粗体,它将具有相同的名称但位于不同的目录中。这样,Android 将根据方向了解并采用正确的布局。

如果我理解了你为什么不想重新创建 Fragment 因为onCreate(Bundle savedInstanceState) 将被再次调用(setRetainInstance(true); 它不会并且关于我之前写的内容你可以试一试)从而创建GetLibraryTask 的新实例并再次下载图像。如果您使用数据库来存储图像并且如果您有一个布尔值来跟踪您是否下载了图像,则可以防止这种情况发生。然后,在GetLibraryTask 中,您将挑选出未下载的图像,无论是第一次运行任务还是方向改变。您还需要在下载循环中的库任务中进行停止检查,在每个项目检查您是否应该下载图像或片段是否不再可用并因此退出任务之前。

现在,当您更改方向时,Activity 将重新创建 LibraryFragment,并根据方向使用 layout 或 layout-land。

代码中的一些附注:

正如我之前写的,永远不要使用公共访问,在必要时始终使用私有或受保护。不过,私有可以一直使用,并且有 getter 和 setter(访问器和修改器)来进行通信。 使用“m”作为成员变量的前缀,在这种情况下,public GridView gridview 将是 private GridView mGridViewprivate Boolean isImageAdapterPopulated 将是 private boolean mIsImageAdapterPopulated 如果不需要,切勿将类用于原始类型。您可能在不支持原始类型或类保留等的列表中需要它。 在您的 onConfigurationChanged(Configuration newConfig) 中,您膨胀了一个 XML,它返回一个视图,但您没有对它做任何事情

祝你好运!

【讨论】:

没有解释的-1?我很迷惑。我做错了什么? @Chirag Patel 我感谢你让事情变得更好的雄心壮志,但老实说,我无法有效地阅读所有粗体文本。 @ChiragPatel 确实,代码高亮用刻度就足够了,也不需要加粗。 嗨,西蒙!谢谢你提醒我编码的约定和最佳实践。方法2在我的情况下似乎很好,我这周必须尝试一下,我认为它会好的。非常感谢这个完整而有用的答案。祝你有美好的一天:) @popov130 很高兴你发现它有帮助!【参考方案2】:

如果您将使用 oncongi 更改方法是可能的。 请看这个链接Change Fragment layout on orientation change

【讨论】:

以上是关于在片段中在运行时更改方向时更改布局而不重新创建视图的主要内容,如果未能解决你的问题,请参考以下文章

如何防止在方向更改时重新创建片段寻呼机中的片段?

在方向更改时保留列表片段中的列表

如何在屏幕方向更改时附加片段?

android片段在方向更改时创建了两次

为不同方向使用不同布局时,在方向更改时保存片段状态

避免Android在方向更改时自动重新添加我的片段