欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 进阶——以一种较为优雅的小框架替代普通回调实现Fragment和Activity之间的通信

程序员文章站 2022-03-09 20:56:39
...

引言

前面两篇文章设计模式——面向对象进阶之面向接口再抽象实现通用的接口框架(一)设计模式——面向对象进阶之面向接口再抽象实现通用的接口框架(二)介绍了个自己封装的一套接口小框架,目的是取代普通回调的常规方式,以期以较优雅的方式来替代常规的接口回调,具体思想和核心代码都再上面两篇文章做了详细介绍,这篇文章就直接使用那套小框架实现Fragment和Activity之间通信。

一、底部导航栏控件BottomNavigationView

在使用那套小框架前,先插入一些底部导航栏BottomNavigationView的简单介绍,BottomNavigationView是Material Design Components 推出的位于support.design 包的官方Material Design 风格的底部导航栏控件,提供不多于 5 个菜单的底部导航栏实现(BottomNavigationView 只支持 3 到 5 个子菜单数量的导航栏。并且,考虑到用户体验,3 个及3个以下菜单数量的导航栏,与超过 3 个时,交互过程也有所区分。关于最多支持 5 个字菜单的内容,可以从 BottomNavigationView 源码中查看:public static final int MAX_ITEM_COUNT = 5;当超出这个数量时,产生非法参数异常)。

1、BottomNavigationView的使用

1.1、首先引入对应的库

implementation 'com.android.support:design:26.1.0'

1.2、在布局文件中声明定义BottomNavigationView

  • app:menu ——引用的menu资源布局文件名

  • app:itemIconTint——Icon 图标着色,值为一个 ColorStateList ,可以在 color 资源文件夹中定义。使用这个属性,奇妙利用 tint 着色器实现一个图标多种状态下使用

  • app:itemTextColor——Label 文字颜色定义

  • app:itemBackground——背景内容

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    tools:context="com.crazymo.universallinteface.MainActivity">

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_nav_main"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:background="@android:color/black"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@color/colorstate_bottom_item_color"
        app:itemTextColor="@color/colorstate_bottom_item_color"
        app:itemBackground="@android:color/black"
        app:menu="@menu/menu_bottomview_main"/>

</RelativeLayout>

1.3、在menu下的定义item的布局文件

使用 menu 资源定义菜单内容,所以这里的item对应的xml布局文件一定要在res/menu/ 下建立

<!--res/menu/menu_bottomview_main.xml-->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
    >
    <item
        android:id="@+id/id_bottomv_home"
        android:enabled="true"
        app:showAsAction="ifRoom"
        android:icon="@drawable/ic_bottomview_home"
        android:title="home" />
    <item
        android:id="@+id/id_bottomv_purchase"
        android:enabled="true"
        app:showAsAction="ifRoom"
        android:icon="@drawable/ic_bottomview_purchase"
        android:title="purchase" />
    <item
        android:id="@+id/id_bottomv_more"
        android:enabled="true"
        app:showAsAction="ifRoom"
        android:icon="@drawable/ic_bottomview_more"
        android:title="more" />

</menu>

1.4、在Activity中初始化并监听相应事件

通过 setOnNavigationItemSelectedListener() 方法可以监听不同子菜单的选中切换事件

        mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){

                }
                return false;
            }
        });

二、使用通用接口小框架实现Fragment和Activity之间的通信

package com.crazymo.universallinteface;

import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast;
import java.util.ArrayList;
import crazymo.core.FunctionManager;
import crazymo.core.FunctionWithParamNoResult;

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener,
        MainFragment.OnFragmentListener{
    private static final int FRAGMENT_TAG_MAIN = 0;
    private static final int FRAGMENT_TAG_MORE =2 ;
    private static final int FRAGMENT_TAG_PURCHASE = 1;
    private FrameLayout mLayout;
    private BottomNavigationView mBottomNavigationView;
    private ArrayList<Fragment> mFragments = new ArrayList<>();
    private Fragment mCurrFragment;
    private int currIndex = 0;
    private MainFragment mMainFragment=new MainFragment();
    private MoreFragment mMoreFragment=new MoreFragment();
    private PurchaseFragment mPurchaseFragment=new PurchaseFragment();
    private FunctionManager mManager=FunctionManager.getInstance();

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

    private void init() {
        getViews();
        mBottomNavigationView.setOnNavigationItemSelectedListener(this);
        initFragment();
        changeFragment(0);
    }

    private void getViews() {
        mLayout = findViewById(R.id.fragment_container);
        mBottomNavigationView = findViewById(R.id.bottom_nav_main);
    }


    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.id_bottomv_home:
                changeFragment(0);
                return true;
            case R.id.id_bottomv_purchase:
                changeFragment(1);
                return true;
            case R.id.id_bottomv_more:
                changeFragment(2);
                return true;
            default:
                break;
        }
        return false;
    }

    private void initFragment() {
        mFragments.add(mMainFragment);
        mFragments.add(mPurchaseFragment);
        mFragments.add(mMoreFragment);
    }

    private void changeFragment(int position) {
        switch (position) {
            case FRAGMENT_TAG_MAIN:
                if (mMainFragment == null) {
                    mMainFragment = (MainFragment) mFragments.get(0);
                }
                chooseFragment(mMainFragment);
                currIndex = 0;
                break;
            case FRAGMENT_TAG_PURCHASE:
                if (mPurchaseFragment == null) {
                    mPurchaseFragment = (PurchaseFragment) mFragments.get(2);
                }
                chooseFragment(mPurchaseFragment);
                currIndex = 1;
                break;
            case FRAGMENT_TAG_MORE:
                if (mMoreFragment == null) {
                    mMoreFragment = (MoreFragment) mFragments.get(1);
                }
                chooseFragment(mMoreFragment);
                currIndex = 2;
                break;
            default:
                break;
        }
    }

    private void chooseFragment(Fragment fragment) {
        if (mCurrFragment == fragment) {
            return;
        }
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (fragment.isAdded()) {
            transaction.show(fragment);
        } else {
            transaction.add(R.id.fragment_container, fragment,fragment.getClass().getName());
        }
        if (mCurrFragment != null) {
            transaction.hide(mCurrFragment);
        }
        transaction.commit();
        mCurrFragment = fragment;
        hideFragments();
    }

    private void hideFragments() {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (mFragments != null) {
            for (int i = 0; i < mFragments.size() - 1; i++) {
                if (currIndex != i) {
                    if (!(mFragments.get(i).isHidden())) {
                        transaction.hide(mFragments.get(i));
                    } else {
                        continue;
                    }
                }
            }
        }
    }

    @Override
    public void onFragementListen(String pStr) {
        Toast.makeText(this,pStr,Toast.LENGTH_LONG).show();
    }

    //2、在Activity中实现接口方法逻辑
    public void implFragmentCallback(String tag){
        if(PurchaseFragment.class.getName().equals(tag)){
            FunctionWithParamNoResult<String> withParamNoResult=new FunctionWithParamNoResult<String>(PurchaseFragment.FRAGMENTLISTEN) {
                @Override
                public void function(String pS) {
                    Toast.makeText(MainActivity.this,""+pS,Toast.LENGTH_SHORT).show();
                }
            };
            mManager.addFunciton(withParamNoResult);
        }else if(MoreFragment.class.getName().equals(tag)){

        }
    }
}

PurchaseFragment通过接口小框架与Activity实现通信:

package com.crazymo.universallinteface;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import crazymo.core.FunctionManager;

public class PurchaseFragment extends Fragment {
    public final static String FRAGMENTLISTEN="PurchaseFragment.OnFragmentListener";//1、定义接口
    private Button btn;
    private FunctionManager mManager=FunctionManager.getInstance();

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

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        //2、绑定接口并调用实现接口方法
        if(context instanceof MainActivity){
            ((MainActivity)context).implFragmentCallback(getTag());
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }

    private void init(View pView) {
        btn=pView.findViewById(R.id.btn_click);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //3、调用接口方法
                mManager.invokeFunc(FRAGMENTLISTEN,String.class,"我是从MainFragment传递到Activity的数据");
            }
        });
    }

}

由于是需要与MainActivity通信所以在Activity中实现接口中的方法


public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener,
        MainFragment.OnFragmentListener{


    //4、实现PurchaseFragment的接口方法
    public void implFragmentCallback(String tag){
        if(PurchaseFragment.class.getName().equals(tag)){
            FunctionWithParamNoResult<String> withParamNoResult=new FunctionWithParamNoResult<String>(PurchaseFragment.FRAGMENTLISTEN) {
                @Override
                public void function(String pS) {
                    Toast.makeText(MainActivity.this,""+pS,Toast.LENGTH_SHORT).show();
                }
            };
            mManager.addFunciton(withParamNoResult);
        }
    }
}

三、使用常规的回调接口实现Fragment和Activity之间的通信

MainFragment通过常规的方式实现与MainActivity通信

//1、implement MainFragment的回调接口
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener,
        MainFragment.OnFragmentListener{
    private static final int FRAGMENT_TAG_MAIN = 0;
    private static final int FRAGMENT_TAG_MORE =2 ;
    private static final int FRAGMENT_TAG_PURCHASE = 1;
    private FrameLayout mLayout;
    private BottomNavigationView mBottomNavigationView;
    private ArrayList<Fragment> mFragments = new ArrayList<>();
    private Fragment mCurrFragment;
    private int currIndex = 0;
    private MainFragment mMainFragment=new MainFragment();
    private MoreFragment mMoreFragment=new MoreFragment();
    private PurchaseFragment mPurchaseFragment=new PurchaseFragment();
    private FunctionManager mManager=FunctionManager.getInstance();

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

    private void init() {
        getViews();
        mBottomNavigationView.setOnNavigationItemSelectedListener(this);
        initFragment();
        changeFragment(0);
    }

    private void getViews() {
        mLayout = findViewById(R.id.fragment_container);
        mBottomNavigationView = findViewById(R.id.bottom_nav_main);
    }


    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.id_bottomv_home:
                changeFragment(0);
                return true;
            case R.id.id_bottomv_purchase:
                changeFragment(1);
                return true;
            case R.id.id_bottomv_more:
                changeFragment(2);
                return true;
            default:
                break;
        }
        return false;
    }

    private void initFragment() {
        mFragments.add(mMainFragment);
        mFragments.add(mPurchaseFragment);
        mFragments.add(mMoreFragment);
    }

    private void changeFragment(int position) {
        switch (position) {
            case FRAGMENT_TAG_MAIN:
                if (mMainFragment == null) {
                    mMainFragment = (MainFragment) mFragments.get(0);
                }
                chooseFragment(mMainFragment);
                currIndex = 0;
                break;
            case FRAGMENT_TAG_PURCHASE:
                if (mPurchaseFragment == null) {
                    mPurchaseFragment = (PurchaseFragment) mFragments.get(2);
                }
                chooseFragment(mPurchaseFragment);
                currIndex = 1;
                break;
            case FRAGMENT_TAG_MORE:
                if (mMoreFragment == null) {
                    mMoreFragment = (MoreFragment) mFragments.get(1);
                }
                chooseFragment(mMoreFragment);
                currIndex = 2;
                break;
            default:
                break;
        }
    }

    private void chooseFragment(Fragment fragment) {
        if (mCurrFragment == fragment) {
            return;
        }
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (fragment.isAdded()) {
            transaction.show(fragment);
        } else {
            transaction.add(R.id.fragment_container, fragment,fragment.getClass().getName());
        }
        if (mCurrFragment != null) {
            transaction.hide(mCurrFragment);
        }
        transaction.commit();
        mCurrFragment = fragment;
        hideFragments();
    }

    private void hideFragments() {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (mFragments != null) {
            for (int i = 0; i < mFragments.size() - 1; i++) {
                if (currIndex != i) {
                    if (!(mFragments.get(i).isHidden())) {
                        transaction.hide(mFragments.get(i));
                    } else {
                        continue;
                    }
                }
            }
        }
    }

    @Override
    public void onFragementListen(String pStr) {
        //2、继承常规接口实现回调方法
        Toast.makeText(this,pStr,Toast.LENGTH_LONG).show();
    }
}

在MainFragment重新声明一个回调接口,并且初始化,使用结束之后再进行手动回收避免内存泄漏

public class MainFragment extends Fragment implements View.OnClickListener {
    private OnFragmentListener mListener;
    private Button btn;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_home, container, false);
        init(view);
        return view;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        //3、绑定回调接口
        if (context instanceof OnFragmentListener) {
            mListener = (OnFragmentListener) context;
        } else {
            throw new RuntimeException(context.toString()+ "must implement MainFragment.OnFragmentListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        //5、销毁回调防止内存泄漏
        mListener=null;
    }

    private void init(View view){
        btn=view.findViewById(R.id.btn_click);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //4、调用回调方法
        mListener.onFragementListen("我是从MainFragment传递到Activity的数据");
    }

    public interface OnFragmentListener{
        void onFragementListen(String pStr);
    }
}

对应的定义布局xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    tools:context="com.crazymo.universallinteface.MainActivity">

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

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_nav_main"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:background="@android:color/black"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@color/colorstate_bottom_item_color"
        app:itemTextColor="@color/colorstate_bottom_item_color"
        app:itemBackground="@android:color/black"
        app:menu="@menu/menu_bottomview_main"/>

</RelativeLayout>

fragment_home.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="36sp"
        android:text="Home Page"/>


    <ImageView
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:src="@mipmap/cmo2"/>

    <Button
        android:id="@+id/btn_click"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="doClick"/>

</LinearLayout>

fragment_purchase.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="36sp"
        android:text="PurchaseCar Page"/>

    <Button
        android:id="@+id/btn_click"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="doClick(有参数无返回值)"/>

</LinearLayout>

Android 进阶——以一种较为优雅的小框架替代普通回调实现Fragment和Activity之间的通信

四、通用接口小框架 VS 传统回调接口形式

为了更直观体现我对于两种方式的具体步骤做了个简单的对比表
Android 进阶——以一种较为优雅的小框架替代普通回调实现Fragment和Activity之间的通信