一、碎片概念
可以将碎片理解成一个迷你型活动,碎片通常在平板开发中使用
二、用法
(1)自定义一个类继承自Fragment,建议选择support-v4库中的Fragment,因为它可以让碎片在所有Android系统版本中保持功能一致性。
(2)新建碎片布局
(3)重写Fagment中的onCreateView()方法,然后在这个方法中通过LayoutInflater的inflate()方法将刚才定义的碎片布局动态加载进来(引用书中例子)
public class LeftFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.left_fragment,container,false);
return view;
}
}
(4)在活动的布局中通过fragment标签引入要添加的碎片(通过其中的android:name属性指定自定义的Fragment类)(引用书中例子)
<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:id="@+id/left_fragment"
android:name="com.wuze.fragmenttest.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
(5)replaceFragment()方法动态添加/更换碎片(引用书中例子)
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager=getSupportFragmentManager();//创建待添加的碎片实例,调用getSupportFragmentManager()方法获取FragmentManager
FragmentTransaction transaction=fragmentManager.beginTransaction();//通过调用beginTransaction()方法开启一个事务
transaction.replace(R.id.right_layout,fragment);//replace()向容器内添加或替换碎片,需要传入容器的id和待添加的碎片实例
transaction.addToBackStack(null);//返回栈,按下back回到上一个碎片,参数一般为null
transaction.commit();//调用commit()提交事务
}
(6)创建待添加的碎片实例作为参数传入replaceFragment()
replaceFragment(new AnotherRightFragment());
三、在碎片中使用返回栈
在事务提交之前调用FragmentTransaction的addToBackStack()方法,它可以接收一个名字用于描述返回栈的状态,一般传入null即可
transaction.addToBackStack(null);//返回栈,按下back回到上一个碎片,参数一般为null
四、碎片和活动之间的通信
(1)在活动中调用碎片:
在活动中调用FragmenrManager的findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就可以调用碎片里的方法。
(2)在碎片中调用活动里的方法:
在碎片中调用getActivity()方法可以获得和当前碎片相关联的活动实例。
(3)碎片间通信:
在碎片中可以的到与它相关联的活动,再通过这个活动去获取另外一个碎片的实例,从而实现了碎片间的通信。
五、碎片的生命周期
碎片的生命周期与活动的生命周期类似,有四个状态:
(1)运行状态:当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。
(2)暂停状态:当一个活动进入暂停状态时,与它相关联的可见碎片就会进入到暂停状态。
(3)停止状态:当一个活动进入停止状态时,与它相关联的碎片就会进入停止状态。
(4)销毁状态:活动被销毁,与它相关联的碎片也被销毁。
六、动态加载布局的技巧
(1)使用限定符
(2)使用最小宽度限定符
七、碎片的实践
目录结构如下:
下面按顺序对每个文件做一个解读:
.java
- MainActivity : 显示主布局,直接在onCreate方法中显示activity_main布局
- News : 新闻类,规范每一条新闻(这是一个List列表的泛型类,用来创建适配器)
- NewsContentActivity : 单页模式,创建一个Activity,显示手机端新闻内容
- NewsContentFragment : 新闻内容碎片(继承Fragment)
- NewsTitleFragment : 新闻列表碎片(加载news_title_frag布局)
.xml
- layout / activity_main : 单页模式下的布局,只加载一个新闻标题列表的的碎片
- news_content : 单页模式下的新闻文本内容布局,引入news_content_frag布局(在NewsContentActivity中显示新闻内容)
- news_content_frag : 新闻文本内容布局,单双页共用
- news_item : 标题子项布局
- news_title_frag : 新闻标题列表(RecyclerView)
- layout-sw600dp / activity_main : 双页模式下的布局,加载两个碎片(标题滑动列表+新闻文本内容)
下面放上具体代码:
来源:郭霖《第一行代码》
- MainActivity.java
加载activity_main布局
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
- News.java
新闻类,set 和 get 方法
public class News {
private String title;
private String content;
public void setTitle(String title) {
this.title = title;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public String getTitle() {
return title;
}
}
- NewsContentActivity.java
在onCreate()方法中通过Intent获取到了传入的新闻标题和新闻内容,然后调用FragmentManager的findFragmentById()方法得到NewsContentFragment的实例,然后调用refresh()方法将新闻标题和新闻内容作为参数传进去,就能够实现数据的显示。
public class NewsContentActivity extends AppCompatActivity {
//actionStart()启动活动
public static void actionStart(Context context,String newsTitle,String newsContent){
Intent intent=new Intent(context,NewsContentActivity.class);//intent:用于启动活动、启动服务、发送广播
intent.putExtra("news_title",newsTitle);//onCreate的getIntent通过"news_title"获取newsTitle的内容
intent.putExtra("news_content",newsContent);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_content);
String newsTitle=getIntent().getStringExtra("news_title");//获取传入的新闻标题
String newsContent=getIntent().getStringExtra("news_content");//获取传入的新闻内容
//news_content_fragment
NewsContentFragment newsContentFragment=(NewsContentFragment)getSupportFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(newsTitle,newsContent);//刷新NewsContentFragment界面
}
}
- NewsContentFragment.java
新闻内容碎片;继承Fragment
重写onCreateView()方法,实现对自定义布局的加载
/* 新闻内容碎片 */
public class NewsContentFragment extends Fragment {
private View view;
//在onCreateView()方法加载了刚刚创建的news_content_frag布局
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
view = inflater.inflate(R.layout.news_content_frag,container,false);
return view;
}
//将新闻的标题和内容显示在界面上
public void refresh(String newsTitle,String newsContent){
View visibilityLayout=view.findViewById(R.id.visibility_layout);
visibilityLayout.setVisibility(View.VISIBLE);
//通过findViewById方法获取新闻标题和内容的控件
TextView newsTitleText=(TextView) view.findViewById(R.id.news_title);
TextView newsContentText=(TextView) view.findViewById(R.id.news_content);
newsTitleText.setText(newsTitle);//刷新新闻标题
newsContentText.setText(newsContent);//刷新新闻内容
}
}
- NewsTitleFragment.java
作为展示新闻列表的碎片;
加载news_title_frag布局
在onActivityCreated()方法中判断是双页模式还是单页模式;
(1)如果是双页模式,则刷新NewsContentFragment的内容,匹配 layout-sw600dp /activity_main.xml , 加载双页布局
(2如果是单页模式,直接启动NewContentActivity,因为这个活动就是专门处理单页模式的,匹配 layout / activity_main.xml,加载单页布局
public class NewsTitleFragment extends Fragment{
private boolean isTwoPane;
//重写onCreateView方法,实现对自定义布局的加载
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view=inflater.inflate(R.layout.news_title_frag,container,false);
RecyclerView newsTitleRecyclerView=(RecyclerView)view.findViewById(R.id.news_title_recycler_view);
LinearLayoutManager layoutManager=new LinearLayoutManager(getActivity());
newsTitleRecyclerView.setLayoutManager(layoutManager);
NewsAdapter adapter=new NewsAdapter(getNews());
newsTitleRecyclerView.setAdapter(adapter);
return view;
}
private List<News> getNews(){
List<News>newsList=new ArrayList<>();
for(int i=1;i<=50;i++){
News news=new News();
news.setTitle("This is news title "+i);
news.setContent(getRandomLengthContent("This is news content"+i+"."));
newsList.add(news);
}
return newsList;
}
private String getRandomLengthContent(String content){
Random random=new Random();
int length=random.nextInt(20)+1;
StringBuilder builder=new StringBuilder();
for(int i=0;i<length;i++){
builder.append(content);
}
return builder.toString();
}
//在这个方法判断是双页模式还是单页模式
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
if(getActivity().findViewById(R.id.news_content_layout)!=null){
isTwoPane=true;//可以找到news_content_layout布局,为双页模式
}
else {
isTwoPane=false;//单页模式
}
}
class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{
private List<News> mNewsList;
class ViewHolder extends RecyclerView.ViewHolder{
TextView newsTitleText;
public ViewHolder(View view){
super(view);
newsTitleText=(TextView)view.findViewById(R.id.news_title);
}
}
public NewsAdapter(List<News> newsList){
mNewsList=newsList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.news_item,parent,false);
final ViewHolder holder=new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
News news=mNewsList.get(holder.getAdapterPosition());
if(isTwoPane){
//如果是双页模式,则刷新NewsContentFragment中的内容
NewsContentFragment newsContentFragment=(NewsContentFragment)getFragmentManager().findFragmentById(R.id.news_content_fragment);
newsContentFragment.refresh(news.getTitle(),news.getContent());
}
else {
//如果是单页模式,则直接启动NewContentActivity
NewsContentActivity.actionStart(getActivity(),news.getTitle(),news.getContent());
}
}
});
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder,int position){
News news=mNewsList.get(position);
holder.newsTitleText.setText(news.getTitle());
}
@Override
public int getItemCount(){
return mNewsList.size();
}
}
- layout / activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_title_fragment"
android:name="com.wuze.fragmentbestpractice.NewsTitleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
- news_content.xml
单页模式下的新闻文本布局,引入news_content_frag布局(在NewsContentActivity中显示新闻内容)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.wuze.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
- news_content_frag.xml
显示新闻内容的布局(单双页共用)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/visibility_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="invisible"
>
<TextView
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:textSize="20sp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000"/>
<TextView
android:id="@+id/news_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="15dp"
android:textSize="18sp"/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#000"/>
</RelativeLayout>
- news_item.xml
标题子项(RecyclerView子项的布局)
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/news_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:textSize="18sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp"
android:paddingBottom="15dp"
/>
- news_title_frag.xml
显示新闻列表的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/news_title_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
- layout-sw600dp / activity_main.xml
<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:id="@+id/news_title_fragment"
android:name="com.wuze.fragmentbestpractice.NewsTitleFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"/>
<FrameLayout
android:id="@+id/news_content_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2">
<fragment
android:id="@+id/news_content_fragment"
android:name="com.wuze.fragmentbestpractice.NewsContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</LinearLayout>
作为刚接触 Android 的小白,如以上所述有误,请多多指正,大家共同进步。