不可不会的Fragment

  • Fragment通过getActivity可以获取所在的activity,activity通过FragmentManager的findFragmentById获取Fragment
    Fragment和activity是多对多的关系

fragment嵌入到activity中

A Fragment

public class AFragment  extends Fragment {
    private TextView mTvTitle;
    @Nullable
    @Override // 用来匹配xml文件
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_a,container,false);
        return view;
    }

    @Override//等价于oncreated,在里边对fragment的控件进行设置
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTvTitle=view.findViewById(R.id.tv_title); //拿到R.id.tv_title
    }
}

A xml

<TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="40sp"
        android:layout_gravity="center"
        android:text="我是AFragment"
        android:gravity="center">

    </TextView>

B Fragment

public class BFragment extends Fragment {
    private TextView mTvTitle;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_b,container,false);
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTvTitle=view.findViewById(R.id.tv_title2);
    }
}

<TextView
        android:id="@+id/tv_title2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="40sp"
        android:layout_gravity="center"
        android:text="我是BFragment"
        android:gravity="center">

    </TextView>

A和B基本一致,都是fragment匹配对应的xml,A中内容是"我是AFragment",B中内容是"我是BFragment"

现在有一个ContainerActivity将A装进去,然后通过按钮将A更换成B

Container xml
Button是change按钮
FrameLayout就是用来装fragment的,在activity中不设置暂时不会起作用

	<Button
        android:id="@+id/btn_change"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="更换Fragment"></Button>
    <FrameLayout
        android:id="@+id/fl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/btn_change"></FrameLayout>

ContainerActivity
需要引入AFragment和BFragment的实例
通过getFragmentManager将A添加到fl_container这个FrameLayout中

更换fragment:change按钮设置点击时间(为空实例化),使用replace方法将R.id.fl_container中fragment替换

	private AFragment aFragment;
    private BFragment bFragment;
    private Button change;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_container);
        //实例化AFragment
        aFragment=new AFragment();
        //AFragment添加到Activity中
        getFragmentManager().beginTransaction().add(R.id.fl_container,aFragment).commitAllowingStateLoss();
        change=findViewById(R.id.btn_change);
        change.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if(bFragment==null){
                     bFragment=new BFragment();
                 }
                 getFragmentManager().beginTransaction().replace(R.id.fl_container,bFragment).commitAllowingStateLoss();
             }
         });
    }

getActivity 为null

fragment中有这个方法,是fragment与activity产生联系时会使用的一个方法,为了防止这个方法为空,将作为变量存起来

@Override
    public void onAttach(Context context) {
        super.onAttach(context);
        ac= (Activity) context;
    }
private Activity ac;

然后在onViewCreated做判断,为空 怎么样,不为空怎么样

@Override//等价于oncreated,在里边对fragment的控件进行设置
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if(ac==null){

        }else{

        }
    }

当然,如果没有为空的风险,就不用这样做

activity传参给fragment

延续上面代码中场景
activity传递字符串给fragment,然后在activity中显示该fragment

静态方法,返回aFragment对象,并且通过该方法传参

public static AFragment newInstance(String title){
        AFragment aFragment=new AFragment();
        Bundle bundle=new Bundle();
        bundle.putString("title",title);
        aFragment.setArguments(bundle);
        return aFragment;
    }

实例化的时候调用该方法

//实例化AFragment
        aFragment=AFragment.newInstance("参数");

Fragment回退栈

现在有这样一个场景:

ContainerActivity xml中有个FrameLayout,在activity中将AFragment加到该FrameLayout中
AFragment xml中有 tv_title(TextView:A) change(Button:将FrameLayout改为BFragment) changeT(Button:改变TextView里面值)

问题1:按照之前的技术应该是A replace to B,当A换成B之后,再返回,回不到AFragment,这就需要回退栈
问题2:changeT改变TextView,当B返回到A后,虽然实例化还保存着(实例化的AFragment)但是页面视图被重置(activity传递的数据失效,即改变的TextView回到初始值),不使用replace方法,replace=remove+add,使用hide().add,就可以完整保留原来页面

操作如下

container

<FrameLayout
        android:id="@+id/fl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
</FrameLayout>

将AFragment放入 FrameLayout 这里add方法比之前多一个参数是Tag信息,后面hide方法会用到
//其中还用到了之前的给fragment传参的技术

public class ContainerActivity extends AppCompatActivity {
    private AFragment aFragment;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_container);
        //实例化AFragment
        aFragment=AFragment.newInstance("参数");
        //AFragment添加到Activity中
        getFragmentManager().beginTransaction().add(R.id.fl_container,aFragment,"a").commitAllowingStateLoss();
    }
}

AFragment

AFragment xml

	<TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="40sp"
        android:layout_gravity="center"
        android:text="A"
        android:gravity="center">

    </TextView>
    <Button
        android:id="@+id/btn_change"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="30dp"
        android:text="更换Fragment">

    </Button>
    <Button
        android:id="@+id/btn_changeT"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:textSize="30dp"
        android:text="更换TextVFiew">

    </Button>

AFragment.class

private TextView mTvTitle;
    private Button change;
    private BFragment bFragment;
    private Button changeT;

    public static AFragment newInstance(String title){
        AFragment aFragment=new AFragment();
        Bundle bundle=new Bundle();
        bundle.putString("title",title);
        aFragment.setArguments(bundle);
        return aFragment;
    }

    @Nullable
    @Override // 用来匹配xml文件
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_a,container,false);
        return view;
    }

    @Override//等价于oncreated,在里边对fragment的控件进行设置
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTvTitle=view.findViewById(R.id.tv_title); //拿到R.id.tv_title

        if(getArguments()!=null){
            mTvTitle.setText(getArguments().getString("title"));
        }

        change=view.findViewById(R.id.btn_change);
        change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bFragment==null){
                    bFragment=new BFragment();
                }
                Fragment fragment=getFragmentManager().findFragmentByTag("a");
                if(fragment!=null){
                    getFragmentManager().beginTransaction().hide(fragment).add(R.id.fl_container,bFragment).addToBackStack(null).commitAllowingStateLoss();
                }else {
                    getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
                }
            }
        });
        changeT=view.findViewById(R.id.btn_changeT);
        changeT.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mTvTitle!=null){
                    mTvTitle.setText("新参数");
                }
            }
        });

    }

传参部分

 mTvTitle=view.findViewById(R.id.tv_title); //拿到R.id.tv_title

        if(getArguments()!=null){
            mTvTitle.setText(getArguments().getString("title"));
        }

改为BFragment,这里findFragmentByTag就是通过之前A设置的Tag来获取,如果它存在就使用hide,不存在也就不存在保留界面数据的问题,addToBackStack方法来使得返回时能够回退到原界面

change=view.findViewById(R.id.btn_change);
        change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(bFragment==null){
                    bFragment=new BFragment();
                }
                Fragment fragment=getFragmentManager().findFragmentByTag("a");
                if(fragment!=null){
                    getFragmentManager().beginTransaction().hide(fragment).add(R.id.fl_container,bFragment).addToBackStack(null).commitAllowingStateLoss();
                }else {
                    getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
                }
            }
        });

BFragment

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_title2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="40sp"
        android:layout_gravity="center"
        android:text="我是BFragment"
        android:gravity="center">

    </TextView>

</LinearLayout>

Fragment传递信息给Activity

首先container有一TextView,然后一个FrameLayout设为AFragment(里边有这么一个按钮)
通过接口回调实现
不可不会的Fragment

不可不会的Fragment

AFragment xml

<Button
        android:id="@+id/btn_change"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_gravity="center"
        android:text="更换数据"
        android:textSize="30dp">

    </Button>

AFragment

按钮和接口对象

private Button change;
private  IOnMessageClick listener;

弄个接口

public interface IOnMessageClick{
        void onClick(String text);
    }

与activity产生联系时强转拿到该activity的接口,实现接口回调

@Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try{
            listener=(IOnMessageClick)context;//当与activity建立联系时,将它的该接口传进来
        }catch (ClassCastException e){
            throw new ClassCastException("activity中 未实现onclick方法");
        }
    }

回调该接口传参

@Override//等价于oncreated,在里边对fragment的控件进行设置
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        change=view.findViewById(R.id.btn_change);
        change.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击更换数据按钮,从fragment将activity中的数据改变
                listener.onClick("数据已改变");
            }
        });
    }

Activity
让activity实现这个接口

 @Override
    public void onClick(String text) {
        mTvTitle=findViewById(R.id.tv_title);
        mTvTitle.setText(text);
    }
上一篇:Fragment & ViewPager


下一篇:Android Fragment使用详解