赞
踩
碎片是一种可以嵌入到活动中地UI片段,可以让程序更加合理地利用大屏幕的空间。碎片和活动有些类似,都有着自己的布局和生命周期,可以看作是迷你型的活动。
举个栗子,一个新闻软件,使用RecyclerView控件显示新闻标题列表,点击标题后会跳转到新闻详情,使用了两个活动,如图所示
而如果直接放到平板上,标题列表会横向拉伸,很不美观,较好的设计是将标题列表和详情分别放到两个碎片中,这样就看起来会好很多
分别编写left_fragment.xml和right_fragment.xml两个布局
filename:left_fragment.xml <?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"> <Button android:id="@+id/Button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="按钮" /> </LinearLayout> filename:right_fragment.xml <?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" android:background="#AAAA00"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/TextView1" android:layout_gravity="center_horizontal" android:text="这是一个TextView"/> </LinearLayout>
右边碎片是个屎黄色背景。
然后编写碎片的类,LeftFragment类和RightFragment类,需要继承Fragment类。继承android.support.v4.app.Fragment而不是android.app.Fragment,因为如果使用系统内置的有些低版本会无法使用一些功能,而且v4库在gradle中已经引入过了。
这里只是重写了onCreateView()方法,在其中动态加载布局即可。
filename:LeftFragment.java public class LeftFragment extends Fragment{ @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.left_fragment,container,false); return view; } } filename:RightFragment public class RightFragment extends Fragment{ @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.right_fragment,container,false); return view; } }
然后在主活动布局文件中添加碎片控件,通过android:name来指定类名
filename:first_layout.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:layout_width="0dp" android:id="@+id/fragment1" android:layout_height="match_parent" android:layout_weight="1" android:name="com.example.k.androidpractice_1.LeftFragment" /> <fragment android:id="@+id/fragment2" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:name="com.example.k.androidpractice_1.RightFragment" /> </LinearLayout>
这时候新建一个平板虚拟机
运行程序,效果如图
碎片的强大之处在于可以在程序运行的时候动态添加到活动中。
新建andther_right_fragment.xml
filename:another_right_fragment.xml
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/TextView2"
android:layout_gravity="center_horizontal"
android:text="这是另一个碎片"/>
</LinearLayout>
然后再新建一个AnotherRightFragment.java,继承v4库的Fragment,同样重写onCreateView()方法,动态加载布局
public class AnotherRightFragment extends Fragment{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.another_right_fragment,container,false);
return view;
}
}
修改first_layout.xml,更换右边的碎片控件为FrameLayout,这个布局所有控件默认放置左上角,是最简单的布局,在这里用比较适合。
filename:first_layout.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:layout_width="0dp" android:id="@+id/fragment1" android:layout_height="match_parent" android:layout_weight="1" android:name="com.example.k.androidpractice_1.LeftFragment" /> <FrameLayout android:id="@+id/FrameLayout1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
随后修改FristActivity.java,给Button添加了一个监听,在onCreate()中添加RightFragment类,监听Button,点击按钮切换到AnotherRightFragment碎片类。
在replaceFragment中,切换碎片需要五个步骤
这样就可以实现碎片的切换了。
filename:FirstActivity.java protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.first_layout); Button MyButton=findViewById(R.id.Button1); MyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { replaceFragment(new AnotherRightFragment()); } }); replaceFragment(new RightFragment()); } private void replaceFragment(Fragment fragment){ FragmentManager fragmentManager=getSupportFragmentManager(); FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction(); fragmentTransaction.replace(R.id.FrameLayout1,fragment); fragmentTransaction.commit(); }
这时候再次运行程序,点击Button可以发现右边的碎片成功切换了。
但是此时按返回键的时候会发现程序退出了,此时需要模拟活动的返回栈,即按返回后回到上一个碎片。
可以通过FgmentTranscration中的addToBackStack()方法,将一个事务添加到返回栈中。
修改FirstActivity.java代码,添加fragmentTransaction.addToBackStack(null);
即可
。。。
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.FrameLayout1,fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
。。。
现在再运行程序以后,点击Button切换了碎片,按返回键后先返回到上一个碎片,然后再按一次才退出。
碎片和活动是处于独立的类中,相互不直接影响,没有直接通信的方式,如果想在活动中调用碎片或者在碎片中调用活动,FragmentManager提供了方法。
类似findViewById()的findFragmentById()方法用于从布局文件中获取碎片实例,如RightFragmet rightFragment=(RightFragment)getSupportFragmentManager().findFragmentById(R.id.right_Fragment);
在碎片中调用与之关联的活动:MainActivity activity=(Activity)getActivity();
。如果碎片中需要使用到context对象的时候,也可以使用这个方法,因为Activity本身就是一个context。
所以碎片之间的通信就可以实现了,在碎片中调用与之关联的活动,在活动中获取另一个碎片的实例,进行通信。
活动的生命周期中有四个状态:运行状态、暂停状态、停止状态和销毁状态。同样碎片也有这四个运行状态
活动中有的回调方法碎片中几乎都有,碎片中还有一些额外的方法:
如下图
动态加载碎片的功能很强大,其实只是在布局文件中的添加和替换操作,如果可以根据屏幕分辨率或大小决定加载什么布局用户体验会更好。
在使用平板的时候很多应用都是横屏双页的,手机则是单页,如何判断应该是双页还是单页呢,需要使用到限定符了。
修改layout文件夹下的first_layout.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:layout_width="match_parent"
android:id="@+id/LeftFragment"
android:layout_height="match_parent"
android:name="com.example.k.androidpractice_1.LeftFragment"
/>
</LinearLayout>
新建文件夹layout-large,新建first_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:id="@+id/LeftFragment" android:name="com.example.k.androidpractice_1.LeftFragment" /> <fragment android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:name="com.example.k.androidpractice_1.RightFragment" android:id="@+id/RightFragment" /> </LinearLayout>
会根据设备自动选择是单页还是双页,但是这里可能是我手机太大了?他还认为我是双叶,然后给我都显示出来了,目前还不知道是为什么,讲道理手机应该只显示按钮的
文件夹名字为layout-sw600dp的时候,当设备屏幕宽度大于等于600dp的时候会加载这个文件夹中的布局。
需要使用到RecyclerView控件,先添加好依赖
compile ‘com.android.support:recyclerview-v7:24.2.1’
编写一个新闻类News.java
filename:News.java public class News { private String Title; private String Content; public String getTitle(){ return Title; } public String getContent(){ return Content; } public void setTitle(String Title){ this.Title=Title; } public void setContent(String Content){ this.Content=Content; } }
然后新建新闻碎片的布局文件news_conteng_frag.xml
filename:news_content_frag.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/visiablity_layout" android:visibility="invisible" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/NewsTitle" android:gravity="center" android:padding="10dp"/> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#000"/> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:id="@+id/NewsContent" android:layout_weight="1" android:padding="15dp"/> </LinearLayout> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="#000"/> </RelativeLayout>
这里主要有两部分,一个是新闻标题,另一个是新闻内容,View是用来画线的,这个RelativeLayout就是左边的标题列表,第二个View是列表右边的竖线,更美观。
新建一个碎片类NewsContentFragment,动态加载碎片布局,然后获取标题和内容的TextView,设置对应的新闻标题和正文
filename:NewsContentFragment.java public class NewsContentFragment extends Fragment{ private View view; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable 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.visiablity_layout); VisibilityLayout.setVisibility(View.VISIBLE); TextView TitleTextView=view.findViewById(R.id.NewsTitle); TextView ContentTextView=view.findViewById(R.id.NewsContent); TitleTextView.setText(NewsTitle); ContentTextView.setText(NewsContent); } }
上面这个是双页模式用的,现在编写单页模式的布局文件,新建活动NewsContentActivity,布局文件为news_content
filename:news_content.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.k.androidpractice_1.NewsContentActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_content_fragment"
android:name="com.example.k.androidpractice_1.NewsContentFragment"/>
</LinearLayout>
修改NewsContentActivity.java内容,通过actionStart(…)方法可以很好的接收参数并且启动活动,在onCreate()中,先从Intent中获取到新闻的标题和正文,随后通过id获取碎片实例,刷新标题和正文。
filename:NewsContentActivity.java public class NewsContentActivity extends AppCompatActivity { public static void actionStart(Context context, String NewsTitle, String NewsContent){ Intent intent=new Intent(context,NewsContentActivity.class); intent.putExtra("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"); NewsContentFragment newsContentFragment=(NewsContentFragment)getSupportFragmentManager().findFragmentById(R.id.news_content_fragment); newsContentFragment.refresh(NewsTitle,NewsContent); } }
新建一个布局文件用于显示新闻列表,news_title_frag.xml
filename:news_title_frag.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/TitlrRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
编写子项布局news_item.xml,只有一个TextView,android:ellipsize
就是说一行显示不下的时候省略
filename:news_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/news_title"
android:ellipsize="end"
android:padding="10dp"
/>
现在需要展示新闻列表的碎片了,新建NewsTitleFragment类,就是把RecyclerView控件动态加载进来了,然后在onActivityCreated()方法中通过对news_content_frag碎片的获取判断当前是双页模式还是单页模式。
filename:NewsTitleFragment.java public class NewsTitleFragment extends Fragment{ private boolean IsTwo; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.news_title_frag,container,false); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (getActivity().findViewById(R.id.news_content_fragment)!=null) IsTwo=true; else IsTwo=false; } }
之后修改主布局文件first_layout.xml内容
filename:first_layout.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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/news_title_frag"
android:name="com.example.k.androidpractice_1.NewsTitleFragment"/>
</LinearLayout>
然后新建一个文件夹layout-sw600dp,新建first_layout.xml,两个碎片,第一个碎片是标题列表,第二个碎片是新闻正文。
filename:layout-sw600dp/first_layout.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:layout_height="match_parent" android:layout_width="0dp" android:layout_weight="1" android:id="@+id/news_title_frag" android:name="com.example.k.androidpractice_1.NewsTitleFragment"/> <FrameLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" > <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.example.k.androidpractice_1.NewsContentFragment" android:id="@+id/news_content_frag"/> </FrameLayout> </LinearLayout>
然后就是最麻烦的了,给标题列表添加适配器,好是好麻烦!!!
filename:NewsTitleFragment.java package com.example.k.androidpractice_1; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; /** * Created by kang on 2020/1/30. */ public class NewsTitleFragment extends Fragment{ boolean IsTwo; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.news_title_frag,container,false); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (getActivity().findViewById(R.id.news_content_fragment)!=null) IsTwo=true; else IsTwo=false; } class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{ private List<News> MyNewsList; class ViewHolder extends RecyclerView.ViewHolder{ TextView MyTextView; public ViewHolder(View view){ super(view); MyTextView=view.findViewById(R.id.news_title); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.news_items,parent,false); final ViewHolder holder=new ViewHolder(view); view.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { News New=MyNewsList.get(holder.getAdapterPosition()); if (IsTwo){ NewsContentFragment newsContentFragment=(NewsContentFragment)getFragmentManager().findFragmentById(R.id.news_content_fragment); newsContentFragment.refresh(New.getTitle(),New.getContent()); }else{ NewsContentActivity.actionStart(getActivity(),New.getTitle(),New.getContent()); } } }); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { News New=MyNewsList.get(position); holder.MyTextView.setText(New.getTitle()); } @Override public int getItemCount() { return MyNewsList.size(); } public NewsAdapter(List<News> MyNewList){ this.MyNewsList=MyNewList; } } }
再次修改NewsTitleFragment类中的onCreateView()方法,添加getNews()方法,为了添加新闻信息,没什么大用
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.news_title_frag,container,false); RecyclerView NewTitleRecyclerView=view.findViewById(R.id.TitlrRecyclerView); LinearLayoutManager linearLayoutManager=new LinearLayoutManager(getActivity()); NewTitleRecyclerView.setLayoutManager(linearLayoutManager); NewsAdapter Adapter=new NewsAdapter(getNews()); NewTitleRecyclerView.setAdapter(Adapter); return view; } private List<News> getNews(){ List<News> NewsList=new ArrayList<>(); for (int i=0;i<50;i++){ News New=new News(); New.setTitle("this is title - "+i); New.setContent("this is title - "+i+"this is title - "+i+"this is title - "+i+"this is title - "+i+"this is title - "+i+"this is title - "+i); NewsList.add(New); } return NewsList; }
之后运行就可以看到结果了,平板上看是这样子的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。