Fragment构建动态UI之构建一个灵活的UI

2018年02月06日 14:32 | 2592次浏览

当你设计你的应用来支持多个屏幕尺寸,你可以基于可用的屏幕空间通过在不同的布局上重用fragment来优化用户体验。


例如,在一个手机上,使用单面板(一次只显示一个 fragment)的用户体验更加合适。For example, on a handset device it might be appropriate to display just one fragment at a time for a single-pane user interface. 相反,你可能希望在一个能够展示更多信息的平板上设置并排摆放的fragments。

图 1. 同一个activity在不同的屏幕尺寸上展示的不同的fragment配置。在大尺寸屏幕上,两个fragment并排展示,而在小屏幕的设备上,一次只能显示一个fragment,需要用户操作来切换它们。


FragmentManager 类提供了能够在运行时add, remove, 以及 replace fragment的方法,这样就能够创建一个动态的用户体验。


在运行时为Activity添加一个Fragment

不同于直接在activity的布局文件中定义fragments(像在上一课中展示的那样,使用,<fragment>元素),你可以在activity运行时添加一个fragment。如果你打算在activity的生命周期期间更改fragment,那么这样做很有必要。


要执行一个添加或者移除fragment操作,你必须使用FragmentManager 创建一个FragmentTransaction,它提供了add, remove, replace, 以及执行其他fragment事务的APIs。


如果你的activity允许fragments被移除或者替换,那么你需要在activity的onCreate()方法中添加初始化这些fragments的方法。


在使用fragment,特别是动态添加的fragment时,有一个重要的规则是fragment必须有一个容器视图,用作fragment的容器。


上一课中展示了一个一次显示一个fragment的布局,下面的布局是上一课中那个布局的可选项。 为了使用fragment之间的切换,这个activity布局中包含了一个空的FrameLayout 作为fragment的容器。


可以注意到这个文件名和上一课中的文件名相同,但是他的布局目录没有包含lager限定符,因此这个布局是给小的屏幕设备使用的,因为这样的屏幕不适用同时展示两个fragment。

res/layout/news_articles.xml:

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

在你的activity中,使用Support Library包的API,调用getSupportFragmentManager() 来获取一个 FragmentManager 。然后调用beginTransaction() 来创建一个FragmentTransaction ,然后调用add() 方法来添加一个fragment。


你可以使用FragmentTransaction为activity执行多个fragment事务。最后(当你准备好改变时),你必须调用commit()方法。


例如,下面是如何为之前的布局添加一个fragment:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create a new Fragment to be placed in the activity layout
            HeadlinesFragment firstFragment = new HeadlinesFragment();
            
            // In case this activity was started with special instructions from an
            // Intent, pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());
            
            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
}

由于这个fragment已经在运行时被动态加入了这个FrameLayout 容器中——而不是在activity的布局文件的<fragment>元素中被定义,这个activity能够移除这个fragment或者替换这个fragment。


替换一个fragment

替换fragment的流程和添加fragment的流程十分相似,不同的是需要调用replace() 方法,而不是add()方法。

要记住当你执行一个fragment事务时,例如替换或者移除一个,这通常会允许用户返回或者撤销这些改变。要允许用户退回一个fragment事务,你必须在提交fragment事务之前调用addToBackStack() 方法。

提示: 当你移除或者替换一个fragment并且添加这个事务到返回栈时,被移除的fragment将会被停止(而 不是被销毁)。如果用户再返回到这个fragment,它会重启(而不是重新创建)。如果你没有将它添加到返回栈,那么这个fragmen在被移除或者替 换时就直接被销毁了。


替换fragment的例子:

// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

addToBackStack() 方法需要传入一个字符串类型的参数来指定这次事务的唯一名称。如果你打算不使用高级的fragment操作APIsFragmentManager.BackStackEntry ,这个名称就是没有作用的。


小说《我是全球混乱的源头》

感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程