Building a Dynamic UI with Fragments

引入

根据android developer Traning里面关于使用Fragment创建动态UI教程,完成一个既适应平板电脑(Tablet)又适应手持机(Handset)UI设计。理想的效果图如下:
这里写图片描述

分析

  1. Tablet一般采用large型号的layout,而Handset一般采用的普通型号的layout.根据Supporting Different Devices 该项目设计包括默认的layout和large的layout。项目结构如下:

    Myproject/
        res/
            layout/
                main.xml
            layout-large/
                main.xml
  2. 添加Fragment组建有两种方式:一种方式Add to Fragment to an Activity using XML,另一种方式Add a Fragment to an Activity at Runtime。相对较好的设计是Tablet采用第一种方式,Handset采用第二种方式。
  3. 涉及个Fragment组建之间的通信时候,两个Fragment不能直接通信,要通过它们附属的Activity作为中间媒介进行通信。具体参考Communicating with Other Fragments

实现

  • layout实现
    res/layout-large/new_articles.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="com.smartfly.fragmentbasics.HeadlinesFragment"
        android:id="@+id/headlines_fragment"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

    <fragment android:name="com.smartfly.fragmentbasics.ArticleFragment"
        android:id="@+id/article_fragment"
        android:layout_weight="2"
        android:layout_width="0dp"
        android:layout_height="match_parent" />

</LinearLayout>

res/layout/news_articles.xml

<?xml version="1.0" encoding="utf-8"?>

<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" />

res/layout/article_view.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/article"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:textSize="18sp"
    />
  • Fragment组建实现
    src/main/java/com/smartfly/fragementbasics/HeadlinesFragment.java
package com.smartfly.fragmentbasics;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

/**
 * Created by public on 2015/12/14.
 */
public class HeadlinesFragment extends ListFragment{

    OnHeadlineSelectedListener mCallback;

    //The container Activity must implement this interface so the frag can deliver messages
    public interface OnHeadlineSelectedListener {
        /**Called by HeadlinesFragment when a list item is selected*/
        public void onArticleSelected(int position);
    }

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

        // We need to use a different list item layout for device older than Honeycomb
        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;

        // Create an array adapter for the list view, using the Ipsum headline array
        setListAdapter(new ArrayAdapter<String>(getActivity(), layout, Ipsum.Headlines));

    }

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

        //When in two-pane layout, set the listview to highlight the selected list item
        //(We do this during onStart because at the point the listview is available)
        if (getFragmentManager().findFragmentById(R.id.article_fragment) != null) {
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        }
    }

    @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 = (OnHeadlineSelectedListener) activity;
        }catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + "must implement OnHeadlinesSelectListener");
        }
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        //Notify the parent activity of selected item
        mCallback.onArticleSelected(position);

        //Set the item as checked to be highlighted when in two-pane layout
        getListView().setItemChecked(position, true);
    }
}

src/main/java/com/smartfly/fragementbasics/ArticleFragment.java

package com.smartfly.fragmentbasics;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**
 * Created by public on 2015/12/14.
 */
public class ArticleFragment extends Fragment {
    final static String ARG_POSITION = "position";
    int mCurrentPosition = -1;
    TextView article;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        //If activity recreated (such as from screen rotate), restore
        //the previous article selection set by onSaveInstanceState().
        //This is primarily necessary when in the two-pane layout.
        if (savedInstanceState != null){
            mCurrentPosition = savedInstanceState.getInt(ARG_POSITION);
        }
        View view = inflater.inflate(R.layout.article_view, container, false);
        article = (TextView) view.findViewById(R.id.article);

        //Inflate the layout for this fragment
        return view;
    }

    public void updateArticleView(int position) {
        article.setText(Ipsum.Articles[position]);
        mCurrentPosition = position;
    }

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

        // During startup, check if there are arguments passed to the fragment.
        // onStart is a good place to do this because the layout has already been
        // applied to the fragment at this point so we can safely call the method
        // below that sets the article text.
        Bundle args = getArguments();
        if (args != null) {
            //Set article based on argument passed in
            updateArticleView(args.getInt(ARG_POSITION));
        } else if (mCurrentPosition != -1) {
            // Set article based on saved instance state defined during onCreateView
            updateArticleView(mCurrentPosition);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        // Save the current article selection in case we need to recreate the fragment
        outState.putInt(ARG_POSITION, mCurrentPosition);
    }
}
  • Activity实现
    src/main/java/com/smartfly/fragementbasics/MainActivty.java
package com.smartfly.fragmentbasics;

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

/**
 * 1.在res/下面存在两个news_articles文件,分别位于/res/layout和/res/layout-large下面
 *
 */
public class MainActivity extends FragmentActivity
        implements HeadlinesFragment.OnHeadlineSelectedListener{

    /** Called when the activity is first created*/
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
        // Check whether the activity is using the layout version with
        // the fragment_container FrameLayout. If so, we must add the first fragment
        if (findViewById(R.id.fragment_container) != null){/*通过fragment_container的id来确认是否加载one-pane layout*/

            // 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 an instance of ExampleFragment
            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();

        }

    }

    @Override
    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment

        //Capture the article fragment from the activity layout
        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null){ /**如果获得article_fragment的id为非空,则表示加载了two-pane layout即layout-large/news_articles*/
            // If articles frag is available, we're in two-pane layout...

            //Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {/**如果获得article_fragemnt的id为空,则表示加载了one-pane layout即layout/news_articles*/
            // If the frag is not available, we're in the one-pane layout and must swap frags...

            //Create fragment and give it an argument for the selected article
            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();
        }
    }
}
  • 辅助类实现
    src/main/java/com/smartfly/fragementbasics/Ipsum.java
package com.smartfly.fragmentbasics;

/**
 * Created by public on 2015/12/14.
 */
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 findViewById()返回为null
    如果在src/main/java/com/smartfly/fragementbasics/ArticleFragment.java中article = (TextView) getActivity.findViewById(R.id.article);容易获得article获得为null.
  • 问题原因
    因为onCreateView()时,fragment已经和activity绑定了,所以说getActivity是有值的,但是我们返回值为空,说明在activity的子控件中找不到fragment的控件。 说明fragment的控件还没有添加到activity中
  • 最佳解决方法
    findViewById()是View对象的方法,先通过inflate()方法得到View,调用这个View对象的getViewById()方法,就能得到这个View树上的子View.

效果

运行效果图:
Android 2.3.7 Handset效果图:
这里写图片描述
这里写图片描述
Android 5.0.0 Hanshset效果图:
这里写图片描述
这里写图片描述
Android 5.0.0 Tablet效果图:
这里写图片描述

参考文献

http://developer.android.com/training/basics/fragments/index.html
http://developer.android.com/guide/components/fragments.html
http://blog.csdn.net/a910626/article/details/46011737

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值