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

引言

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

这里写图片描述

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

为了更直观体现我对于两种方式的具体步骤做了个简单的对比表
这里写图片描述

猜你喜欢

转载自blog.csdn.net/crazymo_/article/details/80017453