第二行代码学习笔记——第四章:手机平板要兼容——探究碎片
来源:互联网 发布:网络媒介素养 名词解释 编辑:程序博客网 时间:2024/05/25 19:57
本章要点
作为一名专业的Android开发人员,能够同时兼容手机和平板的开发时我们必须要做到的事情。
4.1 碎片是什么
碎片(Fragment)是一种可以嵌套在活动当中的UI片段,它能让程序更加合理和充分的利用大屏幕的控件。
4.2 碎片的使用方式
开始我们的碎片之旅,创建FragmentTest项目。
4.2.1 碎片的简易用法
最简单碎片,在一个活动中添加两个碎片,并让这两个碎片平分活动控件。
新建左碎片布局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/btn" android:text="Button" android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
新建右侧碎片,right_fragment.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="#00ff00" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20sp" android:text="This is right fragment"/></LinearLayout>
新建LeftFragment类和RrightFragment类,并让它们继承自support-v4包下的Fragment,重写onCreateView()来加载布局。
LeftFragment,代码如下:
public class LeftFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).inflate(R.layout.left_fragment, container, false); return view; }}
RightFragment,代码如下:
public class RightFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).inflate(R.layout.right_fragment, container, false); return view; }}
修改activity_main.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"> <fragment android:id="@+id/left_fragment" android:name="com.example.hjw.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/> <fragment android:id="@+id/right_fragment" android:name="com.example.hjw.fragmenttest.RightFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/></LinearLayout>
属性:
android:name 指明添加碎片的类名(包名+类名)。
运行效果:
4.2.2 动态添加碎片
新建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:background="#ffff00" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:textSize="20sp" android:text="This is another right fragment" android:layout_gravity="center_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
创建AnotherRightFragment类,代码如下:
public class AnotherRightFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).inflate(R.layout.another_right_fragment, container, false); return view; }}
修改activity_main.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"> <fragment android:id="@+id/left_fragment" android:name="com.example.hjw.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/> <FrameLayout android:id="@+id/right_layout" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent"></FrameLayout></LinearLayout>
实现动态添加Fragment,修改MainActivity中的代码如下所示:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn= (Button) findViewById(R.id.btn); btn.setOnClickListener(this); replaceFragmetn(new RightFragment()); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn: replaceFragmetn(new AnotherRightFragment()); break; default: break;z } } public void replaceFragmetn(Fragment fragment){ FragmentManager fragmentManager=getSupportFragmentManager(); FragmentTransaction transaction=fragmentManager.beginTransaction(); transaction.replace(R.id.right_layout,fragment); transaction.commit(); }}
动态添加布局5步骤:
- 创建待添加碎片的实例。
- 获取FragmentManager,在活动中直接通过调用getSupportFragmentManager()得到。
- 开启一个事务,通过beginTransaction()开启。
- 向容器添加或替换布局,一般使用replace()实现,需要传入容器的id和待添加布局的实例。
- 提交事务,commit()。
重启,点击一下按钮,效果图:
4.2.3 在碎片中模拟返回栈
模仿类似于返回栈的效果好,Back返回上一个碎片。
FragmentTransaction提供了addToBackStack()方法,一般传入null。修改MainActivity中的代码如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { ...... public void replaceFragmetn(Fragment fragment){ FragmentManager fragmentManager=getSupportFragmentManager(); FragmentTransaction transaction=fragmentManager.beginTransaction(); transaction.replace(R.id.right_layout,fragment); transaction.addToBackStack(null); transaction.commit(); }}
重新运行程序,点击按钮将AnotherRightFragment添加到活动中,按下Back键,会RightFragment,再按下Back键,RightFragment也会消失,再按下Back键,程序才会退出。
4.2.4 碎片与活动之间进行通信
活动中调用碎片的方法,FragmentManager提供了一个类似于findFragmentById()的方法,从布局中获取碎片的实例,代码如下:
RightFragment rightFragment = (RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);
碎片中调用活动的方法,调用getActivity()方法来得到和当前碎片相关联的活动,代码如下:
MainActivity activity = (MainActivity) getActivity();
这样,碎片就可以调用活动中的方法了,当碎片需要使用Context对象时,也可以使用getActivity()方法,因为获取到的活动本身就是一个Context对象。
4.3 碎片的生命周期
碎片自己的生命周期。
4.3.1 碎片的状态和回调
碎片和活动一样,生命周期会有4中状态:
- 运行状态
当一个碎片时可见的,并且它关联的活动正处于运行状态时,该对片也处于运行状态。 - 暂停状态
当一个活动进入暂停状态时,与它相关联的可见碎片就会进入到暂停状态。 - 停止状态
当一个活动进入停止状态时,与它相关联的碎片就会进入停止状态,或调用FragmentTransaction的revome(),replace()将碎片从活动中移除, 如果在提交事务之前调用addToBackStack()方法,碎片也会进入停止状态。 - 销毁状态
碎片总是依附于活动而存在的,当活动销毁时,与它关联的碎片也会销毁状态。或调用FragmentTransaction的revome(),replace()将碎片从活动中移除, 如果在提交事务之前并没有调用addToBackStack()方法,这时的碎片就会进入销毁状态。
活动中的回调方法,碎片几乎都有,碎片还提供了一些附加的回调方法:
- onAttach()。 碎片和活动建立关联的时候调用。
- onCreateView()。 碎片创建视图(加载布局)时调用。
- onActivityCreated()。 确保与碎片相关联的活动一定已经创建完毕调用。
- onDestroyView()。 当与碎片相关联的视图被移除的时候调用。
- onDetach()。 当碎片与活动解除关联的时候调用。
碎片完整的生命周期图:
4.3.2 体验碎片的生命周期
修改RightFragment中的代码,如下:
public class RightFragment extends Fragment { private static final String TAG = "RightFragment"; @Override public void onAttach(Context context) { super.onAttach(context); Log.d(TAG, "onAttach: "); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: "); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView: "); View view = LayoutInflater.from(getContext()).inflate(R.layout.right_fragment, container, false); MainActivity activity = (MainActivity) getActivity(); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.d(TAG, "onActivityCreated: "); } @Override public void onStart() { super.onStart(); Log.d(TAG, "onStart: "); } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume: "); } @Override public void onPause() { super.onPause(); Log.d(TAG, "onPause: "); } @Override public void onStop() { super.onStop(); Log.d(TAG, "onStop: "); } @Override public void onDestroyView() { super.onDestroyView(); Log.d(TAG, "onDestroyView: "); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: "); } @Override public void onDetach() { super.onDetach(); Log.d(TAG, "onDetach: "); }}
运行程序,观察logcat打印信息:
点击LeftFragment中的按钮,观察logcat打印信息:
按下Back键,RightFragment重新回到运行状态,观察logcat打印信息:
再次按下Back键,观察logcat打印信息:
4.4 动态加载布局的技巧
Android中动态加载布局的计技巧。
4.4.1 使用限定符
判断程序应该是单页还是双页模式:限定符(Qualifiers)。
修改FragmentTest项目中的activity.main.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:id="@+id/left_fragment" android:name="com.example.hjw.fragmenttest.LeftFragment" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout>
在res目录下layout_large文件夹,新建activity.main.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:id="@+id/left_fragment" android:name="com.example.hjw.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/> <fragment android:id="@+id/right_fragment" android:name="com.example.hjw.fragmenttest.RightFragment" android:layout_width="0dp" android:layout_weight="3" android:layout_height="match_parent"/></LinearLayout>
large是一个限定符,修改Macitivty中的代码:replaceFragment()代码注掉。运行平板效果如下:
在启动一个手机模拟器,运行效果如下:
限定符的参数:
4.4.2 使用最小宽度限定符
最小限定符(Smallest-width-Qualifier)
在res目录新建layout-sw600dp文件夹,新建activity_main.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:id="@+id/left_fragment" android:name="com.example.hjw.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/><fragment android:id="@+id/right_fragment" android:name="com.example.hjw.fragmenttest.RightFragment" android:layout_width="0dp" android:layout_weight="3" android:layout_height="match_parent"/></LinearLayout>
屏幕宽度大于600dp,会加载layout-600dp中的布局,小于600加载默认布局。
4.5 碎片的最佳实践——一个简易版的新闻应用
新闻应用
新建FragmentBestPractice项目。
添加依赖:
compile 'com.android.support:recyclerview-v7:24.2.1'
新建News实体类:
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 getTitle() { return title; } public String getContent() { return content; }}
新建布局文件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:id="@+id/visibility_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_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/tv_news_content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout> <View android:layout_width="1dp" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:background="#000" /></RelativeLayout>
新建NewsContentFragment类加载news_content_frag布局,继承自Fragment,代码如下:
public class NewsContentFragment extends Fragment { private View view; private TextView tv_news_title,tv_news_content; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { view = LayoutInflater.from(getContext()).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); tv_news_title= (TextView) view.findViewById(R.id.tv_news_title); tv_news_content= (TextView) view.findViewById(R.id.tv_news_content); tv_news_title.setText(newsTitle); //刷新新闻的标题 tv_news_content.setText(newsContent); //刷新新闻的头部 }}
单页模式使用,创建NewsContentActivity,指定布局名为news_content,引入NewsContentFragment布局,修改布局文件如下:
<?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" android:orientation="vertical"> <fragment android:id="@+id/news_content_fragment" android:name="com.example.hjw.fragmentbestpractice.NewsContentFragment" android:layout_width="match_parent" android:layout_height="match_parent"/></LinearLayout>
修改NewsContentActivity 中的代码,如下:
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); Intent intent = getIntent(); String news_title = intent.getStringExtra("news_title"); //获取新闻的标题 String news_content = intent.getStringExtra("news_content"); //获取新闻的内容 NewsContentFragment newsContentFragment= (NewsContentFragment) getSupportFragmentManager().findFragmentById(R.id.news_content_fragment); newsContentFragment.refresh(news_title,news_content); //刷新NewsContentFragment界面 }}
新建news_title_frag.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" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/news_title_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
新闻标题的子布局,新建news_item.xml作为RecyclerView中子项的布局,代码如下:
<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tv_news_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="end" android:paddingBottom="15dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="15dp" android:singleLine="true" android:textSize="18sp" />
新建NewsTitleFragment作为展示新闻列表的碎片,onCreateView加载news_title_frag.xml布局,代码如下:
public class NewsTitleFragment extends Fragment{ private boolean isTowPane; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).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_layout)!=null){ isTowPane=true; //可以找到news_content_layout布局,为双页 }else{ isTowPane=false; //找不到news_content_layout布局,为单页 }}
接下来我们修改main_activity.xml,单页模式中的代码如下:
<?xml version="1.0" encoding="utf-8"?><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.example.hjw.fragmentbestpractice.NewsTitleFragment" android:layout_width="match_parent" android:layout_height="match_parent"/></FrameLayout>
新建layout-600dp文件夹,新建main_activity.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" android:orientation="horizontal"> <fragment android:id="@+id/news_title_fragment" android:name="com.example.hjw.fragmentbestpractice.NewsTitleFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/news_content_layout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3"> <fragment android:id="@+id/news_content_fragment" android:name="com.example.hjw.fragmentbestpractice.NewsContentFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout></LinearLayout>
接下来在NewsTitleFragment中通过RecyclerView展示新闻列表,在NewsTitleFragment中新建内部类NewsAdapter作为RecyclerView的适配器,代码如下所示:
public class NewsTitleFragment extends Fragment{ private boolean isTowPane; ...... class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder>{ private List<News> mData; public NewsAdapter(List<News> mData) { this.mData = mData; } @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 = mData.get(holder.getAdapterPosition()); if (isTowPane){ //如果是双页,刷新NewsContentFragment中的内容 NewsContentFragment newsContentFragment= (NewsContentFragment) getFragmentManager().findFragmentById(R.id.news_content_fragment); newsContentFragment.refresh(news.getTitle(),news.getContent()); }else{ //如果是单页,直接启动NewsContentActivity NewsContentActivity.actionStart(getContext(),news.getTitle(),news.getContent()); } } }); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { News news = mData.get(position); holder.tv_news_title.setText(news.getTitle()); } @Override public int getItemCount() { return mData.size(); } class ViewHolder extends RecyclerView.ViewHolder{ TextView tv_news_title; public ViewHolder(View itemView) { super(itemView); tv_news_title= (TextView) itemView.findViewById(R.id.tv_news_title); } } }}
通过onCreateViewHolder方法中注册的点击事件,获取到点击项News的实例,通过isTwoPane判断是单页还是双页,更新里面的数据,修改NewsTitleFragment中的代码如下:
public class NewsTitleFragment extends Fragment{ private boolean isTowPane; private RecyclerView news_title_recycler_view; List<News> newsList=new ArrayList<>(); @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = LayoutInflater.from(getContext()).inflate(R.layout.news_title_frag, container, false); news_title_recycler_view = (RecyclerView) view.findViewById(R.id.news_title_recycler_view); LinearLayoutManager layoutManager=new LinearLayoutManager(getContext()); news_title_recycler_view.setLayoutManager(layoutManager); NewsAdapter adapter = new NewsAdapter(getNews()); news_title_recycler_view.setAdapter(adapter); return view; } public List<News> getNews() { 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(); } ...}
运行效果:
单页模式的运行效果:
点击子选项跳转:
双页模式的运行效果图:
4.5 小结与点评
本章我们了解碎片的基本概念,以及使用场景,掌握了碎片的常用方法,学习了碎片的生命周期,以及动态加载布局。
- 第二行代码学习笔记——第四章:手机平板要兼容——探究碎片
- 阅读郭林《第一行代码》的笔记——第4章 手机平板要兼顾,探究碎片
- 第四章 手机平板要兼顾-探究碎片
- 平板也要兼容——碎片
- 第4章 手机平板要兼顾,探究碎片
- 手机平板要兼顾,探究碎片
- 手机平板要兼顾,探究碎片
- 手机平板要兼顾,探究碎片
- 第一代码第二版(郭霖著)笔记之第四章(探究碎片)
- 第一行代码笔记,第四章-------探究碎片
- 第二行代码学习笔记——第二章:先从看得到的入手——探究活动
- 第二行代码学习笔记——第二章:先从看得到的入手——探究活动
- 第二行代码学习笔记——第七章:跨程序共享数据——探究内容提供器
- 第二行代码学习笔记——第十章:后台默默的劳动者——探究服务
- 第二行代码学习笔记——第八章:丰富你的程序——运行手机多媒体
- android代码碎片(—)
- Android学习笔记——碎片
- 第二行代码第四章笔记
- windows下wamp安装php-redis扩展
- 大型网站架构之分布式消息队列
- Unity 3D
- Java系列-Set、List、Map的遍历总结
- pyqt注销操作后重新登录--程序的重启
- 第二行代码学习笔记——第四章:手机平板要兼容——探究碎片
- linux pci扫描流程
- 游戏开发之在UE4中编写C++代码控制角色
- WININET编程获得cookie的sessionID并保持sessionID不过期
- 第一章 数据引用与匿名存储
- VMware Workstation Pro与win7网络互通设置
- eclipse远程调试hadoop程序
- bindingResult
- 5、Linux系统的目录结构