引入
根据android developer Traning里面关于使用Fragment创建动态UI教程,完成一个既适应平板电脑(Tablet)又适应手持机(Handset)UI设计。理想的效果图如下:
分析
Tablet一般采用large型号的layout,而Handset一般采用的普通型号的layout.根据Supporting Different Devices 该项目设计包括默认的layout和large的layout。项目结构如下:
Myproject/ res/ layout/ main.xml layout-large/ main.xml
- 添加Fragment组建有两种方式:一种方式Add to Fragment to an Activity using XML,另一种方式Add a Fragment to an Activity at Runtime。相对较好的设计是Tablet采用第一种方式,Handset采用第二种方式。
- 涉及个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.youkuaiyun.com/a910626/article/details/46011737