Android成长之路(5)——利用Fragment创建一个动态的UI

简单的效果图如下:

现在利用碎片实现一个简单的动态UI,点击左边标题栏的标题,然后左边正文栏显示对应的文章

1、在activity_main.xml布局中添加两个Fragment。

一个对应左边的标题栏,一个对应右边的正文栏

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
>

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        class="com.example.fragment.HeadlinesFragment"/>

    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:id="@+id/articles_fragment"
        class="com.example.fragment.ArticleFragment"/>

</LinearLayout>

2、然后,需要创建一个类来放置内容。内容就是左边的标题和右边的正文了。

public class Ipsum {

    static String[] Headlines = {
            "Article One",
            "Article Two"
    };

    static String[] Articles = {
            "Article One\n\nExcepteur pour-over occaecat squid biodiesel umami gastropub, nulla laborum salvia dreamcatcher fanny pack. Ullamco culpa retro ea, trust fund excepteur eiusmod direct trade banksy nisi lo-fi cray messenger bag. Nesciunt esse carles selvage put a bird on it gluten-free, wes anderson ut trust fund twee occupy viral. Laboris small batch scenester pork belly, leggings ut farm-to-table aliquip yr nostrud iphone viral next level. Craft beer dreamcatcher pinterest truffaut ethnic, authentic brunch. Esse single-origin coffee banksy do next level tempor. Velit synth dreamcatcher, magna shoreditch in american apparel messenger bag narwhal PBR ennui farm-to-table.",
            "Article Two\n\nVinyl williamsburg non velit, master cleanse four loko banh mi. Enim kogi keytar trust fund pop-up portland gentrify. Non ea typewriter dolore deserunt Austin. Ad magna ethical kogi mixtape next level. Aliqua pork belly thundercats, ut pop-up tattooed dreamcatcher kogi accusamus photo booth irony portland. Semiotics brunch ut locavore irure, enim etsy laborum stumptown carles gentrify post-ironic cray. Butcher 3 wolf moon blog synth, vegan carles odd future."
    };
}

在创建Fragment类之前,先来看看fragment的生命周期。

可以看到,在fragment中,也有onCreate()、onStart()这些方法

由图可以清楚地知道fragment的生命周期

fragment是生命周期与Activity的生命周期。

fragment是嵌入在activity里面,它的生命周期会受到主activity生命周期的直接影响。

比如,当activity处于paused状态,它里面所有的fragment也是处于pasued这一状态。

3、首先来创建一个ArticleFragment类,继承Fragment。这个Fragment是右边的正文栏部分

public class ArticleFragment extends Fragment {

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

}

碎片通常用作一个活动的用户界面的一部分,并有助于提高布局的灵活性。

需要通过实现onCreateView()回调方法,来绘制fragment的布局,必须要返回一个View值。

而这个布局可以在XML文件中定义,调用onCreateView() 提供的一个 LayoutInflater对象,传入布局的id。

onCreateView()方法传入了三个参数。

看看官方的解释:

The container parameter passed to onCreateView() is the parent ViewGroup (from the activity’s layout) in which your fragment layout will be inserted.

The savedInstanceState parameter is a Bundle that provides data about the previous instance of the fragment, if the fragment is being resumed (restoring state is discussed more in the section about Handling the Fragment Lifecycle).

inflate() 方法也有三个参数:

The resource ID of the layout you want to inflate.

The ViewGroup to be the parent of the inflated layout. Passing the container is important in order for the system to apply layout parameters to the root view of the inflated layout, specified by the parent view in which it’s going.

A boolean indicating whether the inflated layout should be attached to the ViewGroup (the second parameter) during inflation. (In this case, this is false because the system is already inserting the inflated layout into the container—passing true would create a redundant view group in the final layout.)

可以看到savedInstanceStat是一个Bundle,如果 fragmen重新回到resumed状态(这里就涉及到fragment的生命周期了),它用来存放之前的fragment实例放入的数据。所以,可以通过重载 onSaveInstanceState,来保存当前选择文章的位置。

在ArticleFragment还需要有更新方法,根据标题点击的位置来显示文章。

ArticleFragment中添加代码:

public class ArticleFragment extends Fragment {

    final static String ARG_POSITION = "ArticleFragementPosition";
    int mCurrentPosition = -1; //先把当前的位置设置为-1,即什么都还没选
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
        }
        return inflater.inflate(R.layout.article_view,container,false);
    }

     @Override
    public void onStart() {
        super.onStart();
        // 在OnStart中,检查是否传递过来参数,确保调用方法的时候是安全的
        Bundle args = getArguments();
        if (args != null)
            updateArticleView(args.getInt(ARG_POSITION));
        } else if (mCurrentPosition != -1) {
            updateArticleView(mCurrentPosition);
        }
    }

    public void updateArticleView(int position) {
        TextView article = (TextView) getActivity().findViewById(R.id.article);
        article.setText(Ipsum.Articles[position]);
        mCurrentPosition = position;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // 保存当前的位置
        outState.putInt(ARG_POSITION, mCurrentPosition);
    }
}

定义的正文栏布局:

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/article"
                android:padding="16dp"
                android:textSize="18sp">
            </TextView>

        </ScrollView>

</LinearLayout>

然后创建一个HeadlinesFragment类,继承ListFragment。这个Fragment是左边的标题部分。

public class HeadlinesFragment extends ListFragment {

}

如果fragment继承的是ListFragment,就已经默认实现了onCreateView()方法,并返回一个ListView,所以这里不需要实现onCreateView()方法。

有时候,需要fragment在activity中分享数据。这里有一个很好的办法,在fragment里面定义接口,在主activity中实现这个接口。当activity通过这个接口接收到数据,在需要的时候就可以把数据分享给其他的fragment。

例如,标题栏这部分的fragment点击标题A,这时候会获得标题A的位置,我们需要根据这个位置来显示想对应的文章。在HeadlinesFragment中添加这个接口。

public class HeadlinesFragment extends ListFragment {
...
 public interface OnHeadlinesSelectedListner {
        public void onArticleSelected(int position);
    }
...
}

接着,在标题栏添加上标题。这里fragment继承了ListFragment,需要适配器(Adapter)把标题的内容添加进来,并且在点击的时候要有相应,能够获取点击的位置。这里的位置从0开始,1,2,3…,不过首先得确保Activity实现上面的接口,需要调用onAttach().

public class HeadlinesFragment extends ListFragment {

     OnHeadlinesSelectedListner mcallback;

     public interface OnHeadlinesSelectedListner {
            public void onArticleSelected(int position);
     }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // 确保Activity实现接口
        try{
            mcallback = (OnHeadlinesSelectedListner) activity;
        } catch (ClassCastException e){
            throw new ClassCastException(activity.toString()
                    + " must implement onHeadlinesSelectedListner");
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //根据设备选择list_item的样式
        int layout = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
                android.R.layout.simple_list_item_activated_1 : android.R.layout.simple_list_item_1;
        // 创建一个数组适配器,传人Ipsum类中的Headlines数组
        setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));
    }

     @Override
     public void onListItemClick(ListView l, View v, int position, long id) {
         super.onListItemClick(l, v, position, id);
         //调用接口方法,这样主activity中实现的时候,传入的position就可以被分享了,
         mcallback.onArticleSelected(position);
         //设置item被选择
         getListView().setItemChecked(position,true);
     }

    @Override
    public void onStart() {
       super.onStart();
       // 设置为单选模式
       if (getFragmentManager().findFragmentById(R.id.articles_fragment) != null) {
           getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
       }
    }
}

最后在主activity中实现接口

public class MainActivity extends FragmentActivity implements HeadlinesFragment.OnHeadlinesSelectedListner {

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

    @Override
    public void onArticleSelected(int position) {
        //这里传入参数position,就是HeadlinesFragment点击选中的位置
        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.articles_fragment);
        articleFrag.updateArticleView(position);
    }
}

到这里为止,全部代码都已经写完了。运行成功,就跟示例图片一样了。



相对于直接在activity的布局中直接添加fragment,其实还可以使用一个FragmentLayout作为一个fragment container,然后可以在activity运行的时候,通过transaction来添加或者移除。

<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" />
...
if (findViewById(R.id.fragment_container) != null) {

            if (savedInstanceState != null) {
                return;
            }
            HeadlinesFragment firstFragment = new HeadlinesFragment();
            firstFragment.setArguments(getIntent().getExtras());
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
...

在activity中,调用getSupportFragmentManager() 来获得FragmentManagers. 然后调用beginTransaction() 来创建一个FragmentTransaction,再add() 添加一个 fragment.

可以通过同一个FragmentTransaction来操作多个fragment. 当已经对fragment操作完成了,比如添加或者移除, 最后就调用commit().

...
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
...

BackStack是Activity使用Task来管理的一个栈。

通过调用 addToBackStack(),这次替代的事务就会被保存在返回栈中。因此用户可以按返回键可以看到之前的fragment。

时间: 2024-10-29 19:11:35

Android成长之路(5)——利用Fragment创建一个动态的UI的相关文章

[Android 成长之路]2-实现带图标的ListView

--by San[[email protected]] 在编写列表时,如果是普通的列表,使用ListView + ArrayAdapter 即可实现,但是如果要在每个列表项中文字前配一个图标了,或者在文字后面再配一个图标,这个就可以用 SimpleAdapter 实现了,不要被它的名字欺骗了,It is not simple, it is powerful. 我们以制作一个带图标的菜单列表为例,首先我们创建一个布局文件 menu_frame.xml,里面包含一个 ListView, 代码如下:

Android成长之路-实现监听器的三种方法

第一种:  在Test类中  定义一个类接口OnClickListener 第二种:直接在Test类上写一个接口 其中的this相当于new OnClickListener()对象, 即class test 中的一个对象, 而如果要用这种方式的话,public void onClick 方法必须写在该test类中, 且在开头使用implements OnClickListener, 即this对象可以直接调用该方法 第三种:  匿名内部类 Android成长之路-实现监听器的三种方法

Android官方入门文档[15]重新创建一个Activity活动

Android官方入门文档[15]重新创建一个Activity活动 Recreating an Activity重新创建一个Activity活动 This lesson teaches you to1.Save Your Activity State2.Restore Your Activity State You should also read?Supporting Different Screens?Handling Runtime Changes?Activities 这节课教你1.保存

[安卓基础] 005.创建一个简单的UI

*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; text-decoration: none; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30px; margin-left: -30px; cursor: poin

创建一个动态Web项目:

开始你的Eclipse,然后进入"文件">"新建">"动态Web项目,然后输入项目名称为HelloWorldStruts2和设置其他的选项,在下面的屏幕: 选择在屏幕上的所有默认选项,最后检查生成的web.xml部署描述符选项.这将创建一个动态Web项目在Eclipse中.现在去窗口>显示视图>项目资源管理器,你会看到你的项目窗口中的东西如下: 现在复制以下文件从Struts 2的lib文件夹C:\struts-2.2.3\lib添

Android成长之路(6)——数据持久化处理

保存key-value对--SharedPreferences 如果有比较小的数据是key-value的关系,这些数据需要储存,那么就可以用到ShardPreferences.一个SharedPreferences对象指向一个包含key-value对的文件,它提供一些简单的方法读和写.每个SharedPreferences文件,可以是私有的也可以是共享的. 注意: SharedPreferences 仅仅可以读和写key-value对,不要跟Preference搞混了. 对 SharedPref

Android成长之路(3)——Activity的生命周期(1)

当用户在用一个App的时候,Activity会在生命周期不同的状态下转换. 当用户离开或重新回到一个Activity,我们可以在生命周期的回调函数中来写一些相关的操作.比如,如果用户打开了一个视频播放器,当他直接切换到另外一个App,这时候应该使播放器暂停,断开获取视频资源的网络连接.当用户回来,我们要重新连接网络获取视频资源,让用户在刚才暂停的位置开始重新观看. 接下来,开始去了解非常重要的生命周期的回调函数,学着怎么去用它们来执行一些使用户有更好体验的操作,并且当我们不需要它们时,不要消耗系

Android成长之路(7)——关于隐式Intent的用法

Android其中最重要的特性之一,就是一个应用可以基于"action"来切换到另一个应用.比如,你的应用想要查找地方,在地图上显示.但是不一定要创建一个activity来显示地图,可以使用Intent发起一个请求来查看地址,然后Android系统会启动一个可以显示地图的应用. 之前,会使用到显式的Intent来让一个activity跳转到另一个activity.但是,当想要跳转到一个独立的应用时,比如查看地图,这时候就一定要使用隐式Intent. 创建一个隐式Intent 隐式Int

利用Myeclipse创建一个Swing应用

在我看来,java强大之处在于:Java不仅仅适用于B/S架构的程序,是做服务端的不二之选以外;还可以用来做C/S架构的程序.虽然现在市面上越来越少使用这种技术,被诸如C#之类的语言代替,但swing的确可以实现桌面化的客户端程序.学java的最好还是要知道有swing这个东西,不然当老板问你知不知道Swing.你说不知道会很尴尬.我们总监问我和另一个同事知不知道swing,那个小伙子一脸懵逼的看着我......闲话少叙,我们一起来看一下利用Myeclipse怎么创建一个swing程序.首先准备