android 通用是框架,对导航栏和fragment的封装

这是fragmentation 和 底部的导航栏 进行一些封装,效果就像是微信的页面,上面是碎片,下面是tab。使用了映射的关系,也就是键值对的意思,键对应tab ,键对应的fragmentation非常好看,使用起来非常方便,逻辑清楚,层次分明。效果如图

android 通用是框架,对导航栏和fragment的封装_第1张图片

这个框架使用的fragment 不是原生的 fragment ,而是 fragmentation。不会的也可以去查一下。其实这里使用fragment也是可以的。只需要改一下继承的类就好了。

还有ButterKnife ,使用注解生成 R文件,不需要findViewById。不会使用也没关系,直接按原来的来就好,会的话就更好了。

下面看一下实现:

1,抽象的基类,实际上就是fragment的基类

public abstract class BaseDelegate extends SwipeBackFragment {

    @SuppressWarnings("SpellCheckingInspection")
    private Unbinder mUnbinder = null;

    /**
     * @return 可以是一个View ,也可以是一个Layout的Id,代表一个视图
     */
    public abstract Object setLayout();


    public abstract void onBindView(@Nullable Bundle savedInstanceState, View rootView);

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        final View rootView;
        //返回的是 Layout的 id
        if (setLayout() instanceof Integer) {
            rootView = inflater.inflate((Integer) setLayout(), container, false);
        } else if (setLayout() instanceof View) {
            rootView = (View) setLayout();
        } else {
            throw new ClassCastException("setLayout() type must be int or view");
        }
        //绑定 资源(ButterKnife)
        mUnbinder = ButterKnife.bind(this, rootView);
        //子类 实现,传入 Bundle 和 碎片视图
        onBindView(savedInstanceState, rootView);

        return rootView;
    }

    /**
     * @return 返回一个 Activity
     */
    public final ProxyActivity getProxyActivity(){
        return (ProxyActivity) _mActivity;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if (mUnbinder != null) {
            mUnbinder.unbind();
        }
    }
}
public abstract class LatteDelegate extends PermissionCheckerDelegate{
	//这里没用,但是可以在这里进行一些权限的操作
}

上面进行了一些简单的封装。

2,fragment 界面的基类,如果要显示某个页面,就需要继承自这个类

/**
 * Copyright (C)
 *
 * @file: BottomItemDelegate
 * @author: 345
 * @Time: 2019/4/25 19:26
 * @description: 导航栏对应的 页面
 */
public abstract class BottomItemDelegate extends LatteDelegate implements View.OnKeyListener {
    private long mExitTime = 0;
    private static final int EXIT_TIME = 2000;

    @Override
    public void onResume() {
        super.onResume();
        final View rootView = getView();
        if (rootView != null) {
            rootView.setFocusableInTouchMode(true);
            rootView.requestFocus();
            rootView.setOnKeyListener(this);
        }
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
            if ((System.currentTimeMillis() - mExitTime) > EXIT_TIME) {
                Toast.makeText(_mActivity, "双击退出" + getString(com.wang.avi.R.string.app_name), Toast.LENGTH_SHORT).show();
                mExitTime = System.currentTimeMillis();
            } else {
                _mActivity.finish();
                if (mExitTime != 0) {
                    mExitTime = 0;
                }
            }
            return true;
        }
        return false;
    }
}

3,tab 的封装,也就是导航栏,其实就是一个bean类,导航栏有几个tab,就new几个对象。(注意这个的导航栏使用了矢量图 第三方库ionicons),不了解的可以先看一下这篇文章

**
 * Copyright (C)
 *
 * @file: BottomTabBean
 * @author: 345
 * @Time: 2019/4/25 19:40
 * @description: 导航栏 的Tab
 */
public final class BottomTabBean {
    /**
     * 图标
     */
    private final CharSequence ICON;
    /**
     * 文字
     */
    private final CharSequence TITLE;

    public BottomTabBean(CharSequence ICON, CharSequence TITLE) {
        this.ICON = ICON;
        this.TITLE = TITLE;
    }

    public CharSequence getIcon() {
        return ICON;
    }

    public CharSequence getTitle() {
        return TITLE;
    }
}

4,创建一个 ItemBuilder 类,用来创造键值对,相当于一个工厂类。用来让tab 和 fragment 产生 映射关系。

/**
 * Copyright (C)
 *
 * @file: ItemBuilder
 * @author: 345
 * @Time: 2019/4/25 19:42
 * @description: 工厂类,用来构建 导航栏 和 碎片
 */
public final class ItemBuilder {
    private final LinkedHashMap<BottomTabBean,BottomItemDelegate> ITEMS = new LinkedHashMap<>();

    static ItemBuilder builder(){
        return new ItemBuilder();
    }

    public final ItemBuilder addItem(BottomTabBean bean,BottomItemDelegate delegate){
        ITEMS.put(bean,delegate);
        return this;
    }

    public final ItemBuilder addItem(LinkedHashMap<BottomTabBean,BottomItemDelegate> items){
        ITEMS.putAll(items);
        return this;
    }

    public final LinkedHashMap<BottomTabBean,BottomItemDelegate> build(){
        return ITEMS;
    }
}

5,对键值对 进行一些封装和使用。通果上面的ItemBuilder 类拿到要显示的所有 fragment和 tab 的类。然后进行一些逻辑的处理。这是一个抽象类。还需要一个子类来实现一些方法。比如setItems()方法 添加需要的 map 。对tab进行监听。对fragment进行处理。他的子类就是用来创建 所有的键值对的。

/**
 * Copyright (C)
 *
 * @file: BaseBottomDelegate
 * @author: 345
 * @Time: 2019/4/25 19:24
 * @description: 对所有的键值对进行管理,也就是 碎片和tab,这是一个抽象类。
 */
public abstract class BaseBottomDelegate extends LatteDelegate implements View.OnClickListener {

    /**
     * 存储所有的子 Fragment
     */
    private final ArrayList<BottomItemDelegate> ITEM_DELEGATES = new ArrayList<>();
    /**
     * 存储所有的子 TabBean
     */
    private final ArrayList<BottomTabBean> TAB_BEANS = new ArrayList<>();

    /**
     * 存储 Fragment和TabBean 的映射
     */
    private final LinkedHashMap<BottomTabBean, BottomItemDelegate> ITEMS = new LinkedHashMap<>();

    /**
     * 当前Fragment 的位置
     */
    private int mCurrentDelegate = 0;
    /**
     * 进入程序展示 的Fragment
     */
    private int mIndexDelegate = 0;
    /**
     * Tab 的颜色
     */
    private int mClickedColor = Color.RED;

    /**
     * 底部 tab
     */
    @BindView(R2.id.bottom_bar)
    LinearLayoutCompat mBottomBar = null;

    /**
     * @param builder 要添加的 映射
     * @return  返回值为 LinkedHashMap
     */
    public abstract LinkedHashMap<BottomTabBean, BottomItemDelegate> setItems(ItemBuilder builder);

    @Override
    public Object setLayout() {
        return R.layout.delegate_bottom;
    }

    public abstract int setIndexDelegate();
    /**
     * 该注解表示 这必须是一个颜色
     */
    @ColorInt
    public abstract int setClickedColor();

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mIndexDelegate = setIndexDelegate();
        if (setClickedColor() != 0){
            mClickedColor = setClickedColor();
        }
        //拿到工厂类的实例
        final ItemBuilder builder = ItemBuilder.builder();
        //获取 添加完成的键值对
        final LinkedHashMap<BottomTabBean,BottomItemDelegate> items = setItems(builder);
        //将 键值对 保存在ITEMS 中
        ITEMS.putAll(items);
        //拿到键和值
        for (Map.Entry<BottomTabBean,BottomItemDelegate> item  :ITEMS.entrySet()){
            final BottomTabBean key = item.getKey();
            final BottomItemDelegate value = item.getValue();
            TAB_BEANS.add(key);
            ITEM_DELEGATES.add(value);
        }
    }

    @Override
    public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {
        final int size = ITEMS.size();

        for (int i = 0; i < size; i++) {
            //第一个参数 布局,第二个参数 为给第一个参数加载的布局 设置一个父布局
            LayoutInflater.from(getContext()).inflate(R.layout.bottom_item_icon_text_layout,mBottomBar);
            //返回指定的视图
            final RelativeLayout item = (RelativeLayout) mBottomBar.getChildAt(i);
            //设置每个 item的点击事件 和标记
            item.setTag(i);
            item.setOnClickListener(this);
            //拿到 item 的第一个和 第二个子布局
            final IconTextView itemIcon = (IconTextView) item.getChildAt(0);
            final AppCompatTextView itemTitle  = (AppCompatTextView) item.getChildAt(1);

            //获取 集合中对应的 Tab
            final BottomTabBean bean = TAB_BEANS.get(i);

            //初始化 tab 数据
            itemIcon.setText(bean.getIcon());
            itemTitle.setText(bean.getTitle());
            //判断是否是 当前显示
            if (i == mIndexDelegate){
                itemIcon.setTextColor(mClickedColor);
                itemTitle.setTextColor(mClickedColor);
            }
        }
        //返回一个数组,里边是fragment的。注意fragment 是继承 supportFragment 的,所以这里的集合是这个类型
        //fragmentation 需要我们这样做
        final SupportFragment[] delegateArry = ITEM_DELEGATES.toArray(new SupportFragment[size]);
        //加载多个根fragment ,并显示其中一个,第二个参数为要显示的fragment
        loadMultipleRootFragment(R.id.bottom_bar_delegate_container,mIndexDelegate,delegateArry);
    }

    /**
     * 重置所有颜色
     */
    private void resetColor(){
        //拿到 底部tab的子布局的size
        final int count = mBottomBar.getChildCount();
        for (int i = 0; i < count; i++) {
            final RelativeLayout item = (RelativeLayout) mBottomBar.getChildAt(i);
            final IconTextView itemIcon = (IconTextView) item.getChildAt(0);
            itemIcon.setTextColor(Color.GRAY);
            final AppCompatTextView itemTitle = (AppCompatTextView) item.getChildAt(1);
            itemTitle.setTextColor(Color.GRAY);
        }
    }

    @Override
    public void onClick(View v) {
        final int tag = (int) v.getTag();
        resetColor();
        final RelativeLayout item = (RelativeLayout) v;
        final IconTextView itemIcon = (IconTextView) item.getChildAt(0);
        itemIcon.setTextColor(mClickedColor);
        final AppCompatTextView itemTitle = (AppCompatTextView) item.getChildAt(1);
        itemTitle.setTextColor(mClickedColor);

        //第一次参数 为要显示的,第二个则是要隐藏的
        showHideFragment(ITEM_DELEGATES.get(tag),ITEM_DELEGATES.get(mCurrentDelegate));
        // 记住当前显示 的下标,注意顺序
        mCurrentDelegate = tag;
    }
}

这里有两个布局,一个是整个界面的布局,还有一个是 tab的子布局

delegate_bottom.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.ContentFrameLayout
        android:id="@+id/bottom_bar_delegate_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/bottom_bar"/>

    <android.support.v7.widget.LinearLayoutCompat
        android:id="@+id/bottom_bar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal"/>
</RelativeLayout>

bottom_item_icon_text_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_weight="1"
    android:paddingBottom="6dp"
    android:paddingTop="6dp"
    android:layout_height="match_parent">

    <com.joanzapata.iconify.widget.IconTextView
        android:id="@+id/item_bottom_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"
        android:textSize="25sp"
        android:gravity="center"/>

    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/tv_bottom_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"/>

</RelativeLayout>

6,要显示的 fragment,这里就是用户可以看见的界面了,这些会和对应的tab 一起存进map中,然后被 上面的类进行管理

public class IndexDelegate extends BottomItemDelegate {
    @Override
    public Object setLayout() {
        return R.layout.delegate_index;
    }

    @Override
    public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {

    }
}
public class SortDelegate extends BottomItemDelegate {
    @Override
    public Object setLayout() {
        return R.layout.delegate_sort;
    }

    @Override
    public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {

    }

这里只加了 两个碎片,有需要的可以多加。

对应布局如下

<?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">

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="首页"/>

</LinearLayout>
<?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">

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="分类" />
</LinearLayout>

7,继承自抽象BaseBottomDelegate ,在这个类中 我们可以创建多个 键值对,这些会返回到 上面的第5点的那个类中,进行处理并进行显示。

/**
 * Copyright (C)
 *
 * @file: EcBottomDelegate
 * @author: 345
 * @Time: 2019/4/26 14:26
 * @description: ${DESCRIPTION}
 */
public class EcBottomDelegate extends BaseBottomDelegate {
    @Override
    public LinkedHashMap<BottomTabBean, BottomItemDelegate> setItems(ItemBuilder builder) {
        final LinkedHashMap<BottomTabBean, BottomItemDelegate> items = new LinkedHashMap<>();
        items.put(new BottomTabBean("{fa-home}","主页"),new IndexDelegate());
        items.put(new BottomTabBean("{fa-sort}","分类"),new SortDelegate());
        items.put(new BottomTabBean("{fa-compass}","发现"),new IndexDelegate());
        items.put(new BottomTabBean("{fa-shopping-cart}","购物车"),new IndexDelegate());
        items.put(new BottomTabBean("{fa-user}","我的"),new IndexDelegate());
        return builder.addItem(items).build();
    }

    @Override
    public int setIndexDelegate() {
        return 0;
    }

    @Override
    public int setClickedColor() {
        return Color.parseColor("#ffff8800");
    }
}

在这个类里面可以对全局的 碎片进行管理。如果导航栏要增加一个 tab ,只需要创建一个字体的bean类,和一个碎片类存入map里面可以显示了。

经过上面的这几个步骤,这个通用的 导航栏+fragment 就弄好了。如果以后我们有任何更改的话,就可以在 BaseBottomDelegate类里面进行更改。如果要添加tab 直接new 字体的bean 在创建一个 fragment就好。如果每个每个碎片中有相同的功能,还可以在页面的基类BottomItemDelegate 中进行更改。目前这个类中只加了 双击退出的功能

效果如下所示

android 通用是框架,对导航栏和fragment的封装_第2张图片
android 通用是框架,对导航栏和fragment的封装_第3张图片
android 通用是框架,对导航栏和fragment的封装_第4张图片

你可能感兴趣的:(Android)