Android微信支付宝的底部导航栏是怎么做的?简单的导航栏蕴藏着大智慧!

来源:互联网 发布:南京大学人工智能专业 编辑:程序博客网 时间:2024/04/29 22:06

常见做法1:

套框架


常见做法2:

在底部写一个水平的LinearLayout作为导航栏,每个Item又是一个垂直的LinearLayout。


。。。

不是说上述做法不好。

做法1不能满足个性定制的需求

做法2嵌套层级太多,拖慢了性能;MainActivity中做事太多,负荷太大


正确做法:


为了维护,我们的模块化原子化是前期开发时必不可少的工作。这里体现的原子性是:

1.底部用碎片取代LinerLayout

2.每个item都作为一个自定义控件,都维护(缓存)了主界面布局碎片的实例。

3.切换碎片的形式采用attach和detach,而不是hide和show,无形中减小了负担,增加了容错率。

4.切换碎片不再是在MainActivity中一个一个地去实例化碎片,而是交给自定义的item控件去维护


先看一下自定义Item控件的代码

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="50dp">    <ImageView        android:id="@+id/nav_icon"        android:layout_width="35dp"        android:layout_height="35dp"        android:layout_gravity="center_horizontal"        tools:background="@mipmap/ic_launcher" />    <TextView        android:id="@+id/nav_title"        android:layout_gravity="center_horizontal"        android:layout_width="wrap_content"        android:layout_height="15dp"        tools:text="微信"/></LinearLayout>

java

维护的变量

private Fragment mFragment = null;//这个是你维护的主界面的碎片实例private ImageView mIcon;private TextView mTitle;private Class<?> mClx;private String mTag;

取得xml并加载的形式,this代表这个xml的父布局就是这个自定义view,再用getChildAt的形式取得碎片中的控件,体现了开闭原则

LayoutInflater inflater = LayoutInflater.from(getContext());inflater.inflate(itemLayoutId, this, true);ViewGroup vg = (ViewGroup) this.getChildAt(0);mIcon = (ImageView) vg.getChildAt(0);mTitle = (TextView) vg.getChildAt(1);

全部代码(完全符合开闭原则,使用的时候不需要修改这个自定义控件任何的代码)

public class NavButton extends FrameLayout {    private Fragment mFragment = null;//这个是你维护的主界面的碎片实例    private ImageView mIcon;    private TextView mTitle;    private Class<?> mClx;    private String mTag;    public NavButton(@NonNull Context context) {        super(context);    }    public NavButton(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public NavButton(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public NavButton(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);    }    private int uncheckedTitle;    private int checkedTitle;    private int uncheckedIcon;    private int checkedIcon;    public void init(@DrawableRes int uncheckedIcon, @DrawableRes int checkedIcon, @ColorInt int uncheckedTitle, @ColorInt int checkedTitle,  String s, Class<?> clx, int itemLayoutId) {        LayoutInflater inflater = LayoutInflater.from(getContext());        inflater.inflate(itemLayoutId, this, true);        ViewGroup vg = (ViewGroup) this.getChildAt(0);        mIcon = (ImageView) vg.getChildAt(0);        mTitle = (TextView) vg.getChildAt(1);        mTitle.setText(s);        if (uncheckedTitle == 0) {            this.uncheckedTitle = mTitle.getCurrentTextColor();        } else {            this.uncheckedTitle = uncheckedTitle;            mTitle.setTextColor(uncheckedTitle);        }        this.checkedTitle = checkedTitle;        this.uncheckedIcon = uncheckedIcon;        this.checkedIcon = checkedIcon;        mIcon.setImageDrawable(getResources().getDrawable(uncheckedIcon));        mClx = clx;        mTag = mClx.getName();    }    public Fragment getFragment() {        return mFragment;    }    public Class<?> getClx() {        return mClx;    }    public String getTag() {        return mTag;    }    public void setFragment(Fragment mFragment) {        this.mFragment = mFragment;    }    @Override    public void setSelected(boolean selected) {        super.setSelected(selected);        if (selected) {            mTitle.setTextColor(checkedTitle);            mIcon.setImageDrawable(getResources().getDrawable(checkedIcon));        } else {            mTitle.setTextColor(uncheckedTitle);            mIcon.setImageDrawable(getResources().getDrawable(uncheckedIcon));        }    }}

底部导航栏碎片代码

xml,想修改布局直接去改item的布局即可

<?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">    <com.example.wechat.nav.NavButton        android:id="@+id/nav_chat"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="match_parent"/>    <com.example.wechat.nav.NavButton        android:id="@+id/nav_friends"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="match_parent"/>    <com.example.wechat.nav.NavButton        android:id="@+id/nav_find"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="match_parent"/>    <com.example.wechat.nav.NavButton        android:id="@+id/nav_me"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="match_parent"/></LinearLayout>

java

初始化前清空所有碎片,防止出错

private void clearOldFragment() {    FragmentTransaction transaction = getFragmentManager().beginTransaction();    List<Fragment> fragments = getFragmentManager().getFragments();    if (transaction == null || fragments == null || fragments.size() == 0)        return;    boolean doCommit = false;    for (Fragment fragment : fragments) {        if (fragment != this && fragment != null) {            transaction.remove(fragment);            doCommit = true;        }    }    if (doCommit)        transaction.commitNow();}

切换item的操作

//思路:把将选的item和当前的item作为新老碎片,mCurrent是当前维护的即选中的itemprivate void doSelect(NavButton newNav) {    NavButton oldNav = null;    //拦截点击当前选中的按钮    if (mCurrentNav != null) {        oldNav = mCurrentNav;        if (oldNav == newNav) {            return;        }    }    //这里进行的操作:1.item的切换2.主布局碎片的切换    doTabChanged(oldNav, newNav, mContainerId);    mCurrentNav = newNav;}

doTabChanged

private void doTabChanged(NavButton oldNav, NavButton newNav, int mContainerId) {    FragmentTransaction transaction = getFragmentManager().beginTransaction();    //将设置选中和设置未选中分立开来    if (oldNav != null) {//这里判空主要防止异常情况和第一次显示设置默认item的选中        if (newNav.getFragment() != null) {//这里的newNav要修改为oldNav,我擦,被开源中国的代码坑了!这是bug啊。找了我老半天            transaction.detach(oldNav.getFragment());//切换主布局碎片显示        }        oldNav.setSelected(false);//切换item显示    }    if (newNav != null) {        if (newNav.getFragment() == null) {            Fragment fragment = Fragment.instantiate(getActivity(),                    newNav.getClx().getName(), null);            transaction.add(mContainerId, fragment, newNav.getTag());//给tag加快取得速度            newNav.setFragment(fragment);//交给他去维护,这是一个缓存机制        } else {            transaction.attach(newNav.getFragment());//采用attach和detach比hide,show性能更高        }        newNav.setSelected(true);    }    transaction.commit();}

全部代码

public class NavFragment extends BaseFragment implements View.OnClickListener {    private FragmentManager mFragmentManager;    private Context mContext;    private int mContainerId;    public NavFragment() {    }    @BindView(R.id.nav_chat)    protected NavButton mChat;    @BindView(R.id.nav_friends)    protected NavButton mFriends;    @BindView(R.id.nav_find)    protected NavButton mFind;    @BindView(R.id.nav_me)    protected NavButton mMe;    @Override    protected int getLayoutId() {        return R.layout.fragment_nav;    }    @Override    protected void initData() {        mContainerId = R.id.fl_main_container;        clearOldFragment(mFragmentManager);    }    @Override    protected void initWidget(View mRoot) {
        //这里就是传的参数:未选中图片,选中图片,0(初始文字,0为不设置)。。        mChat.init(R.drawable.nav_chat_unchecked, R.drawable.nav_chat_checked, 0, getResources().getColor(R.color.nav), "微信", ChatFragment.class, R.layout.nav_item);        mFriends.init(R.drawable.nav_friends_unchecked, R.drawable.nav_friends_checked, 0, getResources().getColor(R.color.nav), "通讯录", FriendsFragment.class, R.layout.nav_item);        mFind.init(R.drawable.nav_find_unchecked, R.drawable.nav_find_checked, 0, getResources().getColor(R.color.nav), "发现", FindFragment.class, R.layout.nav_item);        mMe.init(R.drawable.nav_me_unchecked, R.drawable.nav_me_checked, 0, getResources().getColor(R.color.nav), "我", MeFragment.class, R.layout.nav_item);        doSelect(mChat);    }    private NavButton mCurrentNav = null;    //思路:把将选的item和当前的item作为新老碎片,mCurrent是当前维护的即选中的item    private void doSelect(NavButton newNav) {        NavButton oldNav = null;        //拦截点击当前选中的按钮        if (mCurrentNav != null) {            oldNav = mCurrentNav;            if (oldNav == newNav) {                return;            }        }        //这里进行的操作:1.item的切换2.主布局碎片的切换        doTabChanged(oldNav, newNav, mContainerId);        mCurrentNav = newNav;    }    private void doTabChanged(NavButton oldNav, NavButton newNav, int mContainerId) {        FragmentTransaction transaction = getFragmentManager().beginTransaction();        //将设置选中和设置未选中分立开来        if (oldNav != null) {//这里判空主要防止异常情况和第一次显示设置默认item的选中            if (newNav.getFragment() != null) {                transaction.detach(oldNav.getFragment());//切换主布局碎片显示            }            oldNav.setSelected(false);//切换item显示        }        if (newNav != null) {            if (newNav.getFragment() == null) {                Fragment fragment = Fragment.instantiate(getActivity(),                        newNav.getClx().getName(), null);                transaction.add(mContainerId, fragment, newNav.getTag());//给tag加快取得速度                newNav.setFragment(fragment);//交给他去维护,这是一个缓存机制            } else {                transaction.attach(newNav.getFragment());//采用attach和detach比hide,show性能更高            }            newNav.setSelected(true);        }        transaction.commit();    }    private void clearOldFragment(FragmentManager mFragmentManager) {        FragmentTransaction transaction = mFragmentManager.beginTransaction();        List<Fragment> fragments = mFragmentManager.getFragments();        if (transaction == null || fragments == null || fragments.size() == 0)            return;        boolean doCommit = false;        for (Fragment fragment : fragments) {            if (fragment != this && fragment != null) {                transaction.remove(fragment);                doCommit = true;            }        }        if (doCommit)            transaction.commitNow();    }    @OnClick({R.id.nav_chat, R.id.nav_friends, R.id.nav_find, R.id.nav_me})    @Override    public void onClick(View v) {        if (v instanceof NavButton) {            NavButton nav = (NavButton) v;            doSelect(nav);        }    }    // TODO: 2017/11/27 chat双击监听}

效果图(素材找的不是很好)



看起来还没有第三方那些绚丽?但是微信支付宝都是这样的,简单素净,高端app必备。而且其代码的可维护性和重用性才是重中之中。

阅读全文
0 0
原创粉丝点击