• Android学习(三)


    Fragment 碎片

    碎片的简单用法

    1. 新建一个左侧碎片布局 起名为left_fragment.xml

      <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/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content" 
              android:layout_gravity="center_horizontal"
              android:text="button"/>
      
      </LinearLayout>
      
    2. 新建一个右侧碎片布局 起名为right_fragment.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:background="#00ff00">
          <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center_horizontal"
              android:textSize="20sp"
              android:text="这是右侧碎片布局"/>
      </LinearLayout>
      
    3. 新建一个LeftFragment类,并让他继承自Fragment 并重写onCreateView()方法

      public class LeftFragment extends Fragment {
          public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
              return inflater.inflate(R.layout.left_fragment,container,false);
          }
      }
      
      在这里使用了inflate()方法将刚才定义的left_fragment布局动态加载进来。
      
    4. 新建一个RightFragment类,并让他继承自Fragment并重写onCreateView()方法

      public class RightFragment extends Fragment {
          public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
              return inflater.inflate(R.layout.right_fragment,container,false);
          }
      }
      
    5. 然后修改activity_main中的布局代码。

      <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="cn.zbuter.fragmentdemo.LeftFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
          <fragment
              android:id="@+id/right_fragment"
              android:name="cn.zbuter.fragmentdemo.RightFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"
              />
      
      </LinearLayout>
      

    使用标签在布局中添加碎片,使用android:name属性来显示知名要添加的碎片完整类名,这样最简单的碎片Demo就做好了。

    动态添加碎片

    1. 其他步骤与上一个例子相同只需要在activity_main中修改将fragment修改成FrameLayout

      <FrameLayout
          android:id="@+id/right_layout"
          android:layout_width="0dp"
          android:layout_height="match_parent"
          android:layout_weight="1"
      
    2. 修改MainActivity中的代码

      public class MainActivity extends AppCompatActivity {
      
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              Button button = (Button) findViewById(R.id.button);
              button.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      replaceFragment(new RightFragment());
                  }
              });
          }
      
          private void replaceFragment(Fragment fragment){
              FragmentManager fragmentManager = getSupportFragmentManager();
              FragmentTransaction transaction = fragmentManager.beginTransaction();
              transaction.replace(R.id.right_layout,fragment);
              // 点击按钮后这个layout可见
              FrameLayout frameLayout = (FrameLayout)findViewById(R.id.right_layout);
              frameLayout.setVisibility(View.VISIBLE);
              //提交事务
              transaction.commit();
          }
      }
      

    总结:
    动态添加碎片主要分为5步:

    1. 创建待添加的碎片实例
    2. 获取FragmentManager,在活动中可以直接通过getSupportFragment()方法得到
    3. 开启一个事物,通过beginTransaction()方法开启。
    4. 向容器内添加或替换碎片, 一般使用replace()方法实现, 需要传入容器的id和待添加的碎片实例
    5. 提交事务,条用commit()方法来完成。

    在碎片中模拟返回栈

    如果想使用back键返回到上一个碎片 只需要使用FragmentTransaction中提供的addToBackStack()方法 就可以用于将一个事物添加到返回栈中, 修改MainActivity中的replaceFragment代码如下:

    private void replaceFragment(Fragment fragment){
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
            transaction.replace(R.id.right_layout,fragment);
            transaction.addToBackStack(null);
            // 点击按钮后这个layout可见
            FrameLayout frameLayout = (FrameLayout)findViewById(R.id.right_layout);
            frameLayout.setVisibility(View.VISIBLE);
            //提交事务
            transaction.commit();
        }
    

    碎片和活动之间进行通信

    如果想要在活动中调用碎片的方法,或者在碎片中调用活动里面的方法就要使用FragmentManage中提供的类似于findViewById()的方法findFragmentById()这个方法专门用于从布局文件中获取碎片的实例。

    LeftFragment leftFragment = (LeftFragment) getSupportFragmentManager().findFragmentById(R.id.left_fragment);     
    

    每个碎片都可以使用getActivity()方法来获取到活动本身。就是一个Context对象。

    碎片的完整生命周期

    添加一个碎片——onAttach()——onCreate()——onCreateView()——onActivityCreated()——onStart()——onResume()——碎片已激活

    碎片被激活后,有两种情况:

    1. 用户点击返回键或者碎片被移除/替换——onPause()——onStop()——onDestoryView()——onDestory()——onDetach()——碎片被销毁
    2. 当碎片被添加到返回栈,然后被移除/替换——onPause()——onStop()——onDestoryView()——从返回栈中回到上一个碎片——重新执行onCreateView()方法

    碎片的状态和回调

    onAttach() 当碎片和活动建立关联的时候调用。
    onCreateView() 当碎片创建视图(加载布局)时调用。
    onActivityCreated() 确保与碎片相关联的活动一定已经创建完毕的时候调用。
    onDestroyView() 当与碎片关联的视图被移除的时候调用。
    onDetach() 当碎片和活动接触关联的时候调用。
    

    使用限定符

    判断程序应该使用单页模式还是双页模式需要借助 限定符来实现。

    1. 我们修改activity_main中的代码。将多余的代码都删除掉,只留下一个左侧碎片并让他不满整个父布局,

      <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="cn.zbuter.fragmentdemo.LeftFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
      
      </LinearLayout>
      
    2. 在res目录下新建layout-large文件夹 并在这个文件夹下新建一个布局 也叫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/left_fragment"
              android:name="cn.zbuter.fragmentdemo.LeftFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
      
          <fragment
              android:id="@+id/right_fragment"
              android:name="cn.zbuter.fragmentdemo.RightFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
      </LinearLayout>
      

    在layout/activity_main.xml中只包含了一个碎片 这就是单页模式
    在layout-large/activity_main.xml中包含了两个碎片 即双页模式

    其中large就是一个限定符,那些屏幕被认为是large的设备就会自动加载layout-large文件夹下的布局,而屏幕晓的设备还是会加载layout文件夹下的布局

    然后把MainActivity中的replaceFragement()方法里面的代码注释掉,并运行程序 就可以看到 在平板上运行就是双页模式,在手机上运行就是单页模式。

    限定符参考表

    屏幕大小方面

        small 代表提供给小屏幕设备的资源
        normal 代表提供给中等屏幕设备的资源
        large 代表提供给大屏幕设备的资源
        xlarge 代表提供给超大屏幕设备的资源
    

    屏幕特征

        分辨率:
        ldpi 代表提供给低分辨率设备的资源(120dpi以下)
        mdpi 代表提供给中等分辨率设备的资源(120dpi-160dpi)
        hdpi 代表提供给高分辨率设备的资源(160dpi-240dpi)
        xhdpi 代表提供给超高分辨率设备的资源(240dpi-320dpi)
        xxhdpi 代表提供给超超高分辨率设备的资源(320dpi-480dpi)
        方向
        land 提供给横屏设备的
        port 提供给竖屏设备的资源
    

    使用最小宽度限定符:

    最小宽度限定符允许我们对屏幕的宽度制定一个最小值(以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加在一个布局,屏幕宽度小于这个值的化就加载另一个布局。

    1. 在res目录下新建 layout-sw600dp 文件夹, 然后在这个文件夹下新建一个activity_main.xml 与 layout-large/activity_main的代码相同。

      <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="cn.zbuter.fragmentdemo.LeftFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
      
          <fragment
              android:id="@+id/right_fragment"
              android:name="cn.zbuter.fragmentdemo.RightFragment"
              android:layout_width="0dp"
              android:layout_height="match_parent"
              android:layout_weight="1"/>
      </LinearLayout>
      

    这就意味这,当程序运行在屏幕宽度大于等于600dp的设备上时 会加载layout-sw600dp/activity_main布局 当程序运行在屏幕宽度小于600dp的设备上时则仍然加载默认的layout/activity_main布局。

    碎片的小Demo ---- 一个简易新闻应用。

    1. 添加 RecyclerView 的依赖库 在app/build.gradle中添加

      implementation 'com.android.support:recyclerview-v7:26.0.0-alpha1'
      
    2. 新建一个欣慰的实体类 News 代码如下

      public class News {
          private String title;
          private String content;
      
          public News() {
          }
      
          public News(String title, String content) {
              this.title = title;
              this.content = content;
          }
      
          public String getTitle() {
              return title;
          }
      
          public void setTitle(String title) {
              this.title = title;
          }
      
          public String getContent() {
              return content;
          }
      
          public void setContent(String content) {
              this.content = content;
          }
      }
      
    3. 新建布局文件 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:orientation="vertical"
              android:visibility="invisible"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
              <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_weight="1"
                  android:padding="15dp"
                  android:textSize="18sp"
                  android:layout_width="match_parent"
                  android:layout_height="0dp" />
      
          </LinearLayout>
      
          <View
              android:layout_width="match_parent"
              android:layout_height="1dp"
              android:layout_alignParentLeft="true"
              android:background="#000"/>
      
      
      </RelativeLayout>
      

      新闻的布局主要可以分为两个部分, 头部分显示新闻标题,正文部分显示新闻内容,中间用一条细线隔开,

    4. 然后创建一个NewsContentFragment类 继承 Fragment

      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.visibility_layout);
              visibilityLayout.setVisibility(View.VISIBLE);
              TextView newsTitleText = (TextView)view.findViewById(R.id.news_title);
              TextView newsContentText = (TextView)view.findViewById(R.id.news_content);
              newsTitleText.setText(newsTitle); // 刷新新闻的标题
              newsContentText.setText(newsContent); //刷新新闻的内容
      
          }
      }
      

      在onCreateView()方法里加载了一个刚刚创建的news_content_frag布局,接下来由提供了一个refresh()方法 这个方法用于设置新闻标题和内容

    5. 这样我们就把新闻内容的碎片和布局都创建好了。但是他们都是在双页模式中使用的,我们如果要在单页模式中也使用的话,还需要再创建一个活动, 起名NewsContentActivity 然后修改activity_news_content.xml中的代码

      <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_frament"
              android:name="cn.zbuter.newsdemo.NewsContentFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />
      </LinearLayout>
      

      在布局中引用NewsContentFragment,这样就详单与把news_content_frag布局的内容自动加了进来

    6. 修改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_tilte", newsTitle);
              intent.putExtra("news_content",newsContent);
              context.startActivity(intent);
      
          }
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_news_content);
              // 获取传入参数
              String newsTitle = getIntent().getStringExtra("new_title");
              String newsContent = getIntent().getStringExtra("news_content");
              NewsContentFragment newsContentFragment = (NewsContentFragment)getSupportFragmentManager().findFragmentById(R.id.news_content_frament);
              newsContentFragment.refresh(newsTitle,newsContent); //刷新界面
          }
      }
      

      在onCreate()方法中通过Intent获取到了传入的新闻标题和新闻内容,然后调用FragmentManager的findFragmentById()方法得到了NewsContentFragment的实例,接着调用他的refresh()方法将新闻的标题和内容传入, 就可以吧数据先输出来了。

    7. 新建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>
      
    8. 新建news_item.xml

      <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:maxLines="1"
          android:ellipsize="end"
          android:textSize="18sp"
          android:paddingLeft="10dp"
          android:paddingRight="10dp"
          android:paddingTop="15dp"
          android:paddingBottom="15dp"/>
      

      ellipsize 用于设定当文本内容超出控件宽度时,文本的缩略方式,这里的end是指在尾部进行缩略

    9. 修改activity_main中的代码:

      <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_title_fragment"
              android:name="cn.zbuter.newsdemo.NewsTitleFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />
      
      </LinearLayout>
      

      在单页模式下,只会加载一个新闻标题的碎片

    10. 新建一个 layout-land/activity_main 文件来适配横屏模式

      <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="cn.zbuter.newsdemo.NewsTitleFragment"
              android:layout_width="0dp"
              android:layout_weight="1"
              android:layout_height="match_parent" />
          <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_frament"
                  android:name="cn.zbuter.newsdemo.NewsContentFragment"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"/>
          </FrameLayout>
      
      </LinearLayout>
      
    11. 然后创建一个用于展示新闻列表的地方 新建一个NewsTitleFragment作为展示新闻列表的碎片,代码如下:

      public class NewsTitleFragment extends Fragment {
      
          private boolean isTwoPane;
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              View view ;
              view = inflater.inflate(R.layout.news_title_frag, container,false);
              return view;
          }
      
      
          public void onActivityCreated(Bundle savedInstanceState) {
              super.onActivityCreated(savedInstanceState);
              if(getActivity().findViewById(R.id.news_content_layout) != null){  // flag
                  isTwoPane = true; // 可以找到newscontent_layout布局时为双页模式。
              }else{
                  isTwoPane = false; // 不能找到newscontent_layout布局为单页模式。
              }
          }
      }
      

      onActivityCreated()方法通过在活动中能否找到一个id为news_content_layout的View来判断当前是双页模式还是单页模式。

    12. 现在已经完成了绝大部分的工作, 但是还剩下一个最重要的一点就是在NewTitleFragement中通过RecyclerView将新闻列表战术出来。 我们在NewsTitleFragment中新建一个内部类NewsAdapter来作为RecyclerView的适配器

      public class NewsTitleFragment extends Fragment {
          private boolean isTwoPane;
      
          class NewsAdapter extends  RecyclerView.Adapter<NewsAdapter.ViewHolder>{
              private List<News> mNewsList
              class ViewHolder extends RecyclerView.ViewHolder{
                  TextView newsTitleText;
      
                  public ViewHolder(View itemView) {
                      super(itemView);
                      newsTitleText = (TextView) itemView.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_frament);
                              newsContentFragment.refresh(news.getTitle(),news.getContent());
      
                          }else{
                              //如果是单页模式则直接启动NewsContentActivity
                              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();
              }
      
      
          }
      
      
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              View view ;
              view = inflater.inflate(R.layout.news_title_frag, container,false);
              return view;
          }
          public void onActivityCreated(Bundle savedInstanceState) {
              super.onActivityCreated(savedInstanceState);
              if(getActivity().findViewById(R.id.news_content_layout) != null){  // flag
                  isTwoPane = true; // 可以找到newscontent_layout布局时为双页模式。
              }else{
                  isTwoPane = false; // 不能找到newscontent_layout布局为单页模式。
              }
          }
      }
      
    13. 向RecyclerView中填充数据, 修改NewsTitleFragment中的代码:

      public class NewsTitleFragment extends Fragment {
          private boolean isTwoPane;
          class NewsAdapter extends  RecyclerView.Adapter<NewsAdapter.ViewHolder>{
              private List<News> mNewsList;
              class ViewHolder extends RecyclerView.ViewHolder{
                  TextView newsTitleText;
      
                  public ViewHolder(View itemView) {
                      super(itemView);
                      newsTitleText = (TextView) itemView.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_frament);
                              newsContentFragment.refresh(news.getTitle(),news.getContent());
      
                          }else{
                              //如果是单页模式则直接启动NewsContentActivity
                              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();
              }
      
          }
      
      
          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
              View view ;
              view = inflater.inflate(R.layout.news_title_frag, container,false);
              RecyclerView newsTitleRecyclerView = view.findViewById(R.id.news_title_recycler_view);
              LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
              newsTitleRecyclerView.setLayoutManager(linearLayoutManager);
              NewsAdapter newsAdapter = new NewsAdapter(getNews());
              newsTitleRecyclerView.setAdapter(newsAdapter);
              return view;
          }
          private List<News> getNews(){
              List<News> newsList= new ArrayList<>();
              for(int i=1; i<=50; i++){
                  News news = new News();
                  news.setTitle("这是新闻标题"+ i);
                  news.setContent(getRandomLengthContent("这是新闻内容"+ 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();
      
          }
          public void onActivityCreated(Bundle savedInstanceState) {
              super.onActivityCreated(savedInstanceState);
              if(getActivity().findViewById(R.id.news_content_layout) != null){  // flag
                  isTwoPane = true; // 可以找到newscontent_layout布局时为双页模式。
              }else{
                  isTwoPane = false; // 不能找到newscontent_layout布局为单页模式。
              }
          }
      }
      

      这样就可以完成了。





  • 相关阅读:
    Starlink星链计划能与5G抗衡?看一下马斯克吹过的牛逼
    代码安全性和健壮性:如何在if和assert中做选择?
    都说软件架构要分层、分模块,具体应该怎么做(一)
    物联网网关开发:基于MQTT消息总线的设计过程(上)
    Linq 集成化查询(1)
    给自己定位的技术总监
    lucene.net初接触
    人生就像一系统软件
    用Microsoft.Practices.Unity实现简单的依赖注入
    flv播放器参数
  • 原文地址:https://www.cnblogs.com/zbuter/p/10021035.html
Copyright © 2020-2023  润新知