Android第一行代码-Fragment

Posted 钢铁-程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android第一行代码-Fragment相关的知识,希望对你有一定的参考价值。

Fragement(碎片)

Fragment是一种可以嵌入在活动当中的UI片段。可以让程序更加合理和充分的利用大屏幕的空间。和活动相似,同样可以包含布局,同样有自己的生命周期。

Fragment的引出

在这里插入图片描述
在这里插入图片描述

上面展示了同样的代码,可能放在平板上会造成视觉效果上的不足。所以更好的方案是将新闻标题列表界面和新闻详细内容界面分别放在两个碎片中,然后在同一个活动中引入这两个碎片。
在这里插入图片描述

碎片的使用方式(创建类继承Fragment,实现里面onCreateView方法)

在活动中添加两个碎片

left_fragment.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">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:layout_gravity="center_horizontal"
        android:text="Button"/>

</LinearLayout>

right_fragment.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:background="#00ff00"
    android:orientation="vertical">
    <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>

activity_main.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标签在布局中添加了碎片-->
    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment" />
    <fragment
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/right_fragment"
        android:name="com.example.fragmenttest.RightFragment" />

</LinearLayout>

创建LeftFragment类,让其继承Fragment,在创建时有两个Fragment可以选择,一个是系统内置的android.app.fragment,一个是support-v4中的android.support.v4.app.fragment,强烈建议使用support-v4库中的Fragment,因为它可以让碎片在所有Android系统中保持功能一致性。appcompat-v7库会将support-v4库一起引入进来,而build.gradle中已经引入了appcompat-v7的依赖。

在元素中的android:name属性指定了在布局中要实例化的Fragment。当系统创建这个Activity布局时,它实例化在布局中指定的每一个Fragment,并且分别调用onCreateView(),来获取每个Fragment的布局。然后系统会在Activity布局中插入通过元素中声明直接返回的视图。

LeftFragment類:

package com.example.fragmenttest;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class LeftFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment,container,false);
        return view;
    }
}

RightFragment類:

package com.example.fragmenttest;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class RightFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.right_fragment,container,false);
        return view;
    }
}

MainActivity類:

package com.example.fragmenttest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

动态添加碎片(静态引入使用在元素中的android:name属性指定了在布局中要实例化的Fragment,动态引入:transaction.replace(需要传入容器的id,碎片实例))

创建AnotherRightFragment类

package com.example.fragmenttest;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class AnotherRightFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.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"
    android:orientation="horizontal"
    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/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment" />
    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/right_layout"/>

</LinearLayout>

向FrameLayout中添加内容,从而实现动态添加碎片的功能,修改MainActivity中的代码。

onCreate函数会调用replaceFragment函数,添加RightFragment这个碎片,按钮点击会将RightFragment碎片切换成AnotherRightFragment。

package com.example.fragmenttest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(this);
        replaceFragment(new RightFragment());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button:
                replaceFragment(new AnotherRightFragment());
                break;
            default:
                break;
        }
    }
    private void replaceFragment(Fragment fragment){
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.replace(R.id.right_layout,fragment);
        transaction.commit();
    }
}

动态添加碎片的5个步骤

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

在碎片中模拟返回栈(addToBackStack方法)

现在是按下Back直接退出,需要实现按下Back回到上一个碎片。
在FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中。

修改replaceFragment代码


private void replaceFragment(Fragment fragment){
   FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout,fragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

在事务提交之前调用addToBackStack方法,可以接收一个名字用户描述返回栈的状态,一般为null即可。

碎片和活动之间进行通信(getFragmentById用于在布局文件中获取碎片的实例)

如果想要在活动中调用碎片里的方法,或者在碎片中调用活动中的方法,应该如何实现呢?

为了方便碎片和活动之间进行通信,FragmentManager提供了一个类似于findViewById的方法,专门用于从布局文件中获取碎片的实例。

RightFragment rightFragment = (RightFragment)getFragmentManager().findFragmentById(R.id.right_fragment);

调用FragmentManager的findFragmentById方法可以在活动中得到相应碎片的实例。

通过getActivity()方法可以获得和当前碎片相关联的活动实例。有了活动实例,在碎片中调用活动里的方法就变得轻而易举了,另外当碎片中需要使用Context对象的时候,也可以使用getActivity()方法,因为获取到的活动本身就是一个Context对象。

MainActivity activity = (MainActivity)getActivity();

碎片的生命周期

1、碎片的状态和回调

回顾活动的四个状态:

  • 1、运行状态
  • 2、暂停状态
  • 3、停止状态
  • 4、销毁状态

碎片再其生命周期也有几个状态,和活动在一些细小的地方有不同

运行状态(碎片可见,关联活动处于运行状态)

当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。

暂停状态(另一个未占满屏幕的活动添加到了栈顶)

当一个活动进入暂停状态时(由于另一个未占满屏幕的活动被添加到了栈顶),与它相关联的可见碎片就会进入暂停状态。

停止状态(与其关联的活动进入停止状态,或者调用Fragment的remove(),replace()方法将碎片从活动中移除,在这之前没调用addToBackStack())

生命周期相关的几个回调函数

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

在这里插入图片描述

体验碎片的生命周期

在FragmentTest项目的基础上改动,修改RightFragment中的代码:

public class RightFragment extends Fragment{
	public static final String TAG = "RightFragment";
	@Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        Log.d(TAG,"onAttach");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
    }
    
	@Override
	public View onCreateView(Layoutflater inflater,ViewGroup container,Bundle savedInstanceState){
		Log.d(TAG,"onCreateView");
		View view = inflater.inflate(R.layout.right_fragment,container,false);
		return view;
	}
	@Override
	public void onActivityCreated(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");
	}
}

当RightFragment第一次被加载到屏幕上的时候,会依次执行onAttach()、onCreate()、onCreateView()、onActivityCreated()、onStart()和onResume()方法,点击LeftFragment,变换碎片,此时新的Fragment替换了当前Fragment,当前Fragment进入停止状态,因此onPause()、onStop()、onDestroyView()方法会得到执行。如果在替换的时候没有调用addToBackStack()方法,此时的Fragment就会进入销毁状态,onDestroy和onDetach就会得到执行。

如果按下Back建,Fragment回到屏幕,onActivityCreated()、onStart()、onResume()会得到执行。

在碎片中你可以通过onSavedInstanceState()方法来保存数据,因为进入停止状态的碎片可能在系统内存不足的时候被回收,保存下来的数据在onCreate、onCreateView、onActivityCreated这三个方法中你都可以重新得到。

动态加载布局的技巧

现在只是在布局文件中进行一些添加和替换,如果程序能根据分辨率或屏幕大小在运行时来决定加载哪个布局,那么可发挥控件就更多了。

使用限定符(通过限定符决定用双页模式还是单页模式)

很多平板用双页模式,左边显示列表,右边显示内容。通过限定符(Qualifier)来判断使用双页模式还是单页模式。

修改FragmentTest项目中的activity_main.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/left_fragment"
        android:name="com.example.fragmenttest.LeftFragment" />

</LinearLayout>

在res目录下新建layout-large目录,新建一个activity_main.xml布局,包含两个碎片,双页模式

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

<?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_widthAndroid 片段生命周期

Listview 项目未在 Fragment Android 中显示

Android从Fragment跳转Activty

片段中的Android Studio RecyclerView [重复]

《第一行代码Android(第3版)》— Android 书籍

如何从 Android 中的 Fragment 访问 UI 元素?