您的位置:首页 > 移动开发 > 微信开发

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

2017-11-29 02:15 609 查看
常见做法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是当前维护的即选中的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;
}


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必备。而且其代码的可维护性和重用性才是重中之中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: