先上效果图:
实现的效果是:竖屏下,收起左侧导航栏,显示右侧的内容栏,可通过滑动显示左侧的导航栏;横屏下,显示左侧的导航栏和右侧的内容栏,不能通过滑动隐藏导航栏。
首先,我们通过简单的几个步骤实现这个效果:
步骤一:创建布局文件,这里横竖屏使用的是不同的布局文件。在res文件下创建文件夹“layout-land”和"layout-port",前者用于存放横屏下的布局文件,后者用于存放竖屏下的布局文件。在“layout-land”文件夹中创建布局文件:activity_navigation.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">
<FrameLayout
android:id="@+id/ly_menu"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start" />
<FrameLayout
android:id="@+id/ly_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
这里使用固定的布局,左侧的FrameLayout用于显示导航栏,右侧的FrameLayout用于显示主界面.
在“layout-port”文件下创建布局文件:activity_navigation.xml(文件名和layout-land下的文件名一致),如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/ly_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/ly_menu"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="start"/>
</android.support.v4.widget.DrawerLayout>
竖屏下,这里使用了DrawerLayout实现滑动功能。这里需要注意的是:横竖屏布局下的frameLayout的id是一样的,如用于展示导航栏的FrameLayout的id都是ly_menu,这样做的目的是方便activity管理fragment。
步骤二:在activity中引用上述布局,NavigationActivity.java如下:
package com.clement.example.switchscreen;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.clement.example.switchscreen.content.ContentFragment;
import com.clement.example.switchscreen.menu.MenuFragment;
public class NavigationActivity extends AppCompatActivity {
private MenuFragment menuFragment;
private ContentFragment contentFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigation);
//初始化工作,绑定Fragment
initVariables(savedInstanceState) ;
}
/**绑定fragment
* @param savedInstanceState
*/
private void initVariables(Bundle savedInstanceState){
//初始化
if(savedInstanceState==null){
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
contentFragment = new ContentFragment();
menuFragment = new MenuFragment();
fragmentTransaction.add(R.id.ly_content, contentFragment,ContentFragment.class.getSimpleName());
fragmentTransaction.add(R.id.ly_menu,menuFragment,MenuFragment.class.getSimpleName());
fragmentTransaction.commit();
}
//屏幕旋转之后
else{
FragmentManager fragmentManager = getFragmentManager();
contentFragment = (ContentFragment) fragmentManager.findFragmentByTag(ContentFragment.class.getSimpleName());
menuFragment = (MenuFragment)fragmentManager.findFragmentByTag(MenuFragment.class.getSimpleName());
}
}
}
该activity只是简单地绑定fragment。当屏幕发生旋转后,NavigationActivity都会销毁重建,自动加载对应的布局文件,到这里就已经实现了上述效果。
但这样是不够的,试想一下,这里的activity是用于管理fragment的,UI视图都是由fragment处理的,当fragment的视图发生了修改,例如contentFragment上有个编辑框,并且已经输入了“ABC”,这时屏幕发生旋转,我们可以发现“ABC”不见了,所有fragment的UI都变回了最初的状态。
那么首先要解决的问题是:横竖屏切换时,保持fragment视图状态。先看ContentFragment.java:
package com.clement.example.switchscreen.content;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.clement.example.switchscreen.R;
public class ContentFragment extends Fragment {
//保存fragment的view
private View rootView ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设备旋转时保存Fragment的交互状态(activity重建时,fragment不执行onCreate和onDestroy)
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(rootView==null){
rootView = inflater.inflate(R.layout.fragment_content, container, false);
}
return rootView;
}
}
当横竖屏切换时,系统会销毁并重建fragment的UI视图,但不会销毁该fragment的实例,通过全局变量rootView保存视图,用于重建时恢复原视图状态;setRetainInstance(true)的作用是:发生横竖屏切换,activity重建时,fragment不执行onCreate和onDestroy(注意onCreateView还是会执行的),因此一些用于初始化fragment的操作可以放在onCreate中,避免横竖屏切换时再次执行。同理,在onCreateView中也可以通过判断rootView是否为空,执行一些初始化操作或横竖屏切换后的操作。
到这里,上述效果就完全实现了。关于扩展:
一.右侧的内容栏不止一个fragment:
假如希望右侧的内容栏能分割为多个fragment,有两种方向:
1.使用fragment嵌套,将ContentFragment当作父fragment,再往里边添加子fragment;
2.平级关系,不想使用嵌套。比如,现在想把右侧的只有一个fragment的布局修改为,由上下两个fragment组成的布局。新建布局文件:content_layout.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">
<FrameLayout
android:id="@+id/ly_top"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_56"/>
<FrameLayout
android:id="@+id/ly_bottom"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
修改activity_navigation.xml(注意横竖屏布局都要修改),将
<FrameLayout
android:id="@+id/ly_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
修改为:
<include layout="@layout/detail_layout"
android:id ="@+id/detail_layout"/>
在activity中修改一下绑定的fragment的逻辑就可以了。这里之所以使用include,主要是受到竖屏环境下使用了DrawerLayout的限制。
二.activity和fragment间的通信
建议还是把activity当做中转站,fragment之间不直接通信。这样也方便把activity和fragment进行封装;需要注意的一个地方是,横竖屏切换时,activity是销毁重建的,即activity的实例每次横竖屏切换后都是新的。例如,activity和fragment通过接口A进行通信,接口A的实例是fragment的一个变量,这样就需要在fragment的onAttach方法中更新接口A的实例了。