android 好看的导航

2014-06-19 09:40:51 sinyu890807 阅读数 75572

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/25466665


本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文。

http://developer.android.com/guide/topics/ui/actionbar.html


限于篇幅的原因,在上篇文章中我们只学习了ActionBar基础部分的知识,那么本篇文章我们将接着上一章的内容继续学习,探究一下ActionBar更加高级的知识。如果你还没有看过前面一篇文章的话,建议先去阅读Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)


添加Action Provider


和Action View有点类似,Action Provider也可以将一个Action按钮替换成一个自定义的布局。但不同的是,Action Provider能够完全控制事件的所有行为,并且还可以在点击的时候显示子菜单。


为了添加一个Action Provider,我们需要在<item>标签中指定一个actionViewClass属性,在里面填入Action Provider的完整类名。我们可以通过继承ActionProvider类的方式来创建一个自己的Action Provider,同时,Android也提供好了几个内置的Action Provider,比如说ShareActionProvider。


由于每个Action Provider都可以自由地控制事件响应,所以它们不需要在onOptionsItemSelected()方法中再去监听点击事件,而是应该在onPerformDefaultAction()方法中去执行相应的逻辑。


那么我们就先来看一下ShareActionProvider的简单用法吧,编辑menu资源文件,在里面加入ShareActionProvider的声明,如下所示:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.actionbartest.MainActivity" >

    <item
        android:id="@+id/action_share"
        android:actionProviderClass="android.widget.ShareActionProvider"
        android:showAsAction="ifRoom"
        android:title="@string/action_share" />
    ......

</menu>
注意,ShareActionProvider会自己处理它的显示和事件,但我们仍然要记得给它添加一个title,以防止它会在overflow当中出现。


接着剩下的事情就是通过Intent来定义出你想分享哪些东西了,我们只需要在onCreateOptionsMenu()中调用MenuItem的getActionProvider()方法来得到该ShareActionProvider对象,再通过setShareIntent()方法去选择构建出什么样的一个Intent就可以了。代码如下所示:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
	MenuInflater inflater = getMenuInflater();
	inflater.inflate(R.menu.main, menu);
	MenuItem shareItem = menu.findItem(R.id.action_share);
	ShareActionProvider provider = (ShareActionProvider) shareItem.getActionProvider();
	provider.setShareIntent(getDefaultIntent());
	......
	return super.onCreateOptionsMenu(menu);
}

private Intent getDefaultIntent() {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/*");
    return intent;
}

可以看到,这里我们通过getDefaultIntent()方法来构建了一个Intent,该Intent表示会将所有可以共享图片的程度都列出来。重新运行一下程序,效果如下图所示:




细心的你一定观察到了,这个ShareActionProvider点击之后是可以展开的,有点类似于overflow的效果,这就是Action Provider的子菜单。除了使用ShareActionProvider之外,我们也可以自定义一个Action Provider,比如说如果想要建立一个拥有两项子菜单的Action Provider,就可以这样写:

public class MyActionProvider extends ActionProvider {
	
	public MyActionProvider(Context context) {
		super(context);
	}

	@Override
	public View onCreateActionView() {
		return null;
	}

	@Override
	public void onPrepareSubMenu(SubMenu subMenu) {
		subMenu.clear();
		subMenu.add("sub item 1").setIcon(R.drawable.ic_launcher)
				.setOnMenuItemClickListener(new OnMenuItemClickListener() {
					@Override
					public boolean onMenuItemClick(MenuItem item) {
						return true;
					}
				});
		subMenu.add("sub item 2").setIcon(R.drawable.ic_launcher)
				.setOnMenuItemClickListener(new OnMenuItemClickListener() {
					@Override
					public boolean onMenuItemClick(MenuItem item) {
						return false;
					}
				});
	}

	@Override
	public boolean hasSubMenu() {
		return true;
	}

}
这里我们新建了一个MyActionProvider继承自ActionProvider,为了表示这个Action Provider是有子菜单的,需要重写hasSubMenu()方法并返回true,然后在onPrepareSubMenu通过调用SubMenu的add()方法添加子菜单。


接着修改menu资源,在里面加入MyActionProvider的声明:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.actionbartest.MainActivity" >

    <item
        android:id="@+id/action_share"
        android:actionProviderClass="com.example.actionbartest.MyActionProvider"
        android:icon="@drawable/ic_launcher"
        android:showAsAction="ifRoom"
        android:title="@string/action_share" />
    ......

</menu>

现在重新运行一下代码,结果如图所示:




添加导航Tabs


Tabs的应用可以算是非常广泛了,它可以使得用户非常轻松地在你的应用程序中切换不同的视图。而Android官方更加推荐使用ActionBar中提供的Tabs功能,因为它更加的智能,可以自动适配各种屏幕的大小。比如说,在平板上屏幕的空间非常充足,Tabs会和Action按钮在同一行显示,如下图所示:




而如果是在手机上,屏幕的空间不够大的话,Tabs和Action按钮则会分为两行显示,如下图所示:




下面我们就来看一下如何使用ActionBar提供的Tab功能,大致可以分为以下几步:


1. 实现ActionBar.TabListener接口,这个接口提供了Tab事件的各种回调,比如当用户点击了一个Tab时,你就可以进行切换Tab的操作。

2.为每一个你想添加的Tab创建一个ActionBar.Tab的实例,并且调用setTabListener()方法来设置ActionBar.TabListener。除此之外,还需要调用setText()方法来给当前Tab设置标题。

3.最后调用ActionBar的addTab()方法将创建好的Tab添加到ActionBar中。


看起来并不复杂,总共就只有三步,那么我们现在就来尝试一下吧。首先第一步需要创建一个实现ActionBar.TabListener接口的类,代码如下所示:

public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

这段代码并不长,我们简单分析一下。当Tab被选中的时候会调用onTabSelected()方法,在这里我们先判断mFragment是否为空,如果为空的话就创建Fragment的实例并调用FragmentTransaction的add()方法,如果不会空的话就调用FragmentTransaction的attach()方法。


而当Tab没有被选中的时候,则调用FragmentTransaction的detach()方法,将UI资源释放掉。


当Tab被重新选中的时候会调用onTabReselected()方法,如果没有特殊需求的话,通常是不需要进行处理的。


接下来第二步要给每一个Tab创建一个ActionBar.Tab的实例,在此之前要先准备好每个Tab页对应的Fragment。比如说这里我们想创建两个Tab页,Artist和Album,那就要先准备好这两个Tab页对应的Fragment。首先新建ArtistFragment,代码如下所示:

public class ArtistFragment extends Fragment {

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		TextView textView = new TextView(getActivity());
		textView.setText("Artist Fragment");
		textView.setGravity(Gravity.CENTER_HORIZONTAL);
		LinearLayout layout = new LinearLayout(getActivity());
		LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		layout.addView(textView, params);
		return layout;
	}

}

没有什么实质性的代码,只是在TextView中显示了Artist Fragment这个字符串。


然后如法炮制,新建AlbumFragment,代码如下所示:

public class AlbumFragment extends Fragment {
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		TextView textView = new TextView(getActivity());
		textView.setText("Album Fragment");
		textView.setGravity(Gravity.CENTER_HORIZONTAL);
		LinearLayout layout = new LinearLayout(getActivity());
		LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		layout.addView(textView, params);
		return layout;
	}

}
Fragment都准备好了之后,接下来就可以开始创建Tab实例了,创建好了之后则再调用addTab()方法添加到ActionBar当中,这两步通常都是在Activity的onCreate()方法中执行的,代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setTitle("天气");
	ActionBar actionBar = getActionBar();
	actionBar.setDisplayHomeAsUpEnabled(true);
	setOverflowShowingAlways();
	actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
	Tab tab = actionBar
			.newTab()
			.setText(R.string.artist)
			.setTabListener(
					new TabListener<ArtistFragment>(this, "artist",
							ArtistFragment.class));
	actionBar.addTab(tab);
	tab = actionBar
			.newTab()
			.setText(R.string.album)
			.setTabListener(
					new TabListener<AlbumFragment>(this, "album",
							AlbumFragment.class));
	actionBar.addTab(tab);
}

可以看到,这里是使用连缀的写法来创建Tab的。首先调用ActionBar的newTab()方法来创建一个Tab实例,接着调用了setText()方法来设置标题,然后再调用setTabListener()方法来设置事件监听器,最后再调用ActionBar的addTab()方法将Tab添加到ActionBar中。


好了,这样的话代码就编写完了,重新运行一下程序,效果如下图所示:




自定义ActionBar样式


虽说ActionBar给用户提供了一种全局统一的界面风格和操作方式,但这并不意味着所有应用程序的ActionBar都必须要长得一模一样。如果你需要修改ActionBar的样式来更加好地适配你的应用,可以非常简单地通过Android样式和主题来实现。


其实Android内置的几个Activity主题中就已经包含了"dark"或"light"这样的ActionBar样式了,同时你也可以继承这些主题,然后进行更深一步的定制。


1. 使用主题


Android中有两个最基本的Activity主题可以用于指定ActionBar的颜色,分别是:

深色系主题样式的效果如下图所示:



浅色系主题样式的效果如下图所示:



你可以将这些主题应用到你的整个应用程序,也可以只应用于某个Activity。通过在AndroidManifest.xml文件中给<application>或<activity>标签指定android:theme属性就可以实现了。比如:
<application android:theme="@android:style/Theme.Holo.Light" ... />
如果你只想让ActionBar使用深色系的主题,而Activity的内容部分仍然使用浅色系的主题,可以通过声明Theme.Holo.Light.DarkActionBar这个主题来实现,效果如下图所示:



2. 自定义背景


如果想要修改ActionBar的背景,我们可以通过创建一个自定义主题并重写actionBarStyle属性来实现。这个属性可以指向另外一个样式,然后我们在这个样式中重写background这个属性就可以指定一个drawable资源或颜色,从而实现自定义背景的功能。

编辑styles.xml文件,在里面加入一个自定义的主题,如下所示:
<resources>

    <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
        <item name="android:actionBarStyle">@style/MyActionBar</item>
    </style>

    <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
        <item name="android:background">#f4842d</item>
    </style>

</resources>
可以看到,这里我们定义了一个CustomActionBarTheme主题,并让它继承自Theme.Holo.Light。然后在其内部重写了actionBarStyle这个属性,然后将这个属性指向了MyActionBar这个样式,我们在这个样式中又重写了background属性,并给它指定了一个背景色。
现在重新运行一下程序,效果如下图所示:



这样我们就成功修改ActionBar的背景色了。不过现在看上去还有点怪怪的,因为只是ActionBar的背景色改变了,Tabs的背景色还是原来的样子,这样就感觉不太协调。那么下面我们马上就来修改一下Tabs的背景色,编辑styles.xml文件,如下所示:
<resources>

    <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
        <item name="android:actionBarStyle">@style/MyActionBar</item>
    </style>

    <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
        <item name="android:background">#f4842d</item>
        <item name="android:backgroundStacked">#d27026</item>
    </style>

</resources>
可以看到,这里又重写了backgroundStacked属性,这个属性就是用于指定Tabs背景色的。那么再次重新运行程序,效果如下图所示:



3. 自定义文字颜色


现在整个ActionBar的颜色是属于偏暗系的,而ActionBar中文字的颜色又偏偏是黑色的,所以看起来并不舒服,那么接下来我们就学习一下如果自定义文字颜色,将文字颜色改成白色。

修改styles.xml文件,如下所示:
<resources>

    ......

    <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
        ......
        <item name="android:titleTextStyle">@style/MyActionBarTitleText</item>
    </style>

    <style name="MyActionBarTitleText" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
        <item name="android:textColor">#fff</item>
    </style>

</resources>
可以看到,这里在MyActionBar样式里面重写了titleTextStyle属性,并将它指向了另一个自定义样式MyActionBarTitleText,接着我们在这个样式中指定textColor的颜色是#fff,也就是白色。

现在重新运行一下程序,结果如下图所示:



OK,ActionBar标题文字的颜色已经成功改成白色了,那Tab标题的文字又该怎么修改呢?继续编辑styles.xml文件,如下所示:
<resources>

    <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
        <item name="android:actionBarStyle">@style/MyActionBar</item>
        <item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item>
    </style>
    
    <style name="MyActionBarTabText"
           parent="@android:style/Widget.Holo.ActionBar.TabText">
        <item name="android:textColor">#fff</item>
    </style>

</resources>
这里我们在CustomActionBarTheme主题中重写actionBarTabTextStyle属性,并将它指向一个新建的MyActionBarTabText样式,然后在这个样式中重写textColor属性,将颜色指定为白色即可。

重新运行一下程序,结果如下图所示:



4. 自定义Tab Indicator


为了可以明确分辨出我们当前选中的是哪一个Tab项,通常情况下都会在选中Tab的下面加上一条横线作为标识,这被称作Tab Indicator。那么上图中的Tab Indicator是蓝色的,明显和整体风格不相符,所以我们接下来就学习一下如何自定义Tab Indicator。

首先我们需要重写actionBarTabStyle这个属性,然后将它指向一个新建的Tab样式,然后重写background这个属性即可。需要注意的是,background必须要指定一个state-list drawable文件,这样在各种不同状态下才能显示出不同的效果。

那么在开始之前,首先我们需要准备四张图片,分别用于表示Tab的四种状态,如下所示:

                           

这四张图片分别表示Tab选中未按下,选中且按下,未选中未按下,未选中且按下这四种状态,那么接着新建res/drawable/actionbar_tab_indicator.xml文件,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item  android:state_selected="false"
          android:state_pressed="false"
          android:drawable="@drawable/tab_unselected" />
    <item android:state_selected="true"
          android:state_pressed="false"
          android:drawable="@drawable/tab_selected" />
    <item android:state_selected="false"
          android:state_pressed="true"
          android:drawable="@drawable/tab_unselected_pressed" />
    <item android:state_selected="true"
          android:state_pressed="true"
          android:drawable="@drawable/tab_selected_pressed" />

</selector>
四种状态分别引用了四张图片,这样就把state-list drawable文件写好了。接着修改style.xml文件,代码如下所示:
<resources>

    <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
        ......
        <item name="android:actionBarTabStyle">@style/MyActionBarTabs</item>
    </style>
    
    <style name="MyActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView">
        <item name="android:background">@drawable/actionbar_tab_indicator</item>
    </style>

</resources>
这里先是重写了actionBarTabStyle这个属性,并将它指向了另一个自定义样式MyActionBarTabs,接着在这个样式中重写background属性,然后指向我们刚才创建的actionbar_tab_indicator即可。

现在重新运行一下程序,效果如下所示:



可以看到,Tab Indicator的颜色已经变成了白色,这样看上去就协调得多了。

除此之外,Action Bar还有许许多多的属性可以进行自定义,这里我们无法一一涵盖到本篇文章中,更多的自定义属性请参考官方文档进行学习。

好了,本篇文章的讲解就到这里,结合上下两篇,我们已经把ActionBar中最常用的功能都学会了,下篇文章中我会带领大家一起实战Action Bar的用法,感兴趣的朋友请继续阅读 Android ActionBar应用实战,高仿微信主界面的设计 。

关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

微信扫一扫下方二维码即可关注:

        

2018-01-22 09:58:36 qq_32770809 阅读数 12735

先来看看底部导航栏的效果




Android 底部导航栏有很多种写法,例如:

         RadioGroup Tablayout, TabHost  , LinearLayout + ImageView + TextView BottomNavigationView


我们就来看最常用的两种底部导航栏的用法

                                     

                     一. LinearLayout  + ImageView + TextView


    这种是用  LinearLyaout + ImageView + TextView 布局UI,  xml 代码如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <FrameLayout
        android:id="@+id/fl_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/line"/>
    <View
        android:id="@+id/line"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/colorTextGrey"
        android:layout_above="@+id/ll"
        />
    <LinearLayout
        android:id="@+id/ll"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="@color/bg_white"
        android:orientation="horizontal"
        >
        <LinearLayout
            android:id="@+id/ll_tab1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/iv1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/selector_page1" />
            <TextView
                android:id="@+id/tv1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:gravity="center"
                android:text="@string/menu_bottom_navigation1"
                android:textColor="@color/colorTextGrey"
                android:textSize="11sp" />
        </LinearLayout>
        <LinearLayout
            android:id="@+id/ll_tab2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/iv2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/selector_page2" />
            <TextView
                android:id="@+id/tv2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:gravity="center"
                android:text="@string/menu_bottom_navigation2"
                android:textColor="@color/colorTextGrey"
                android:textSize="11sp" />
        </LinearLayout>
        <LinearLayout
            android:id="@+id/ll_tab3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">
            <ImageView
                android:id="@+id/iv3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/selector_page3" />
            <TextView
                android:id="@+id/tv3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="2dp"
                android:gravity="center"
                android:text="@string/menu_bottom_navigation3"
                android:textColor="@color/colorTextGrey"
                android:textSize="11sp" />
        </LinearLayout>
    </LinearLayout>
</android.support.percent.PercentRelativeLayout>

     此布局最外层是百分比布局 PercentRelativeLayout (适配各种机型屏幕), 而后   是一个

     帧布局 FrameLayout(用于Fragment),最后就是这个布局的重点 :  父LinearLayout

<LinearLayout
    android:id="@+id/ll"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_alignParentBottom="true"
    android:background="@color/bg_white"
    android:orientation="horizontal"
   >

   这个LinearLayout  ,  宽为match_paren, 高为 50dp ,  位于父布局底部,子布局水平排列。

   有人就会有疑问了:为什么 高为50dp?

   因为底部导航栏其实有两种不同标准的高,一个 48dp , 一个 56dp(通过不断积累的经验:这样好看)。

   而我给50dp, 算是取个中间整数。

  下面看 父 LinearLayout 里面的 第一个 子 LinearLayout

<LinearLayout
    android:id="@+id/ll_tab1"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    android:gravity="center"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/iv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/selector_page1" />
    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="2dp"
        android:gravity="center"
        android:text="@string/menu_bottom_navigation1"
        android:textColor="@color/colorTextGrey"
        android:textSize="11sp" />
</LinearLayout>

 

这个LinearLayout  ,  宽为0dp, 高为 match_paren , 权重为1(为了让 子LinearLayout 平分

总LinearLayout 的宽),内容居中, 子布局垂直排列:里面有 ImageView 和 TextView。

第一个子LinearLayout效果:       

        

                              

   依葫芦画瓢,按第一个 子Linearlayout 写 第二个 子LinearLayout 和 第三个 子LinearLayout 


   总效果:



          


   selector_page1 代码:


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/img_1" android:state_enabled="true"></item>
    <item android:drawable="@mipmap/img_1_selected" android:state_enabled="false"></item>

 selector_page2,selector_page3依葫芦画瓢


 下面看Activity 代码:



public class BottomNavigationActivity1 extends AppCompatActivity {

    @BindView(R.id.fl_fragment)
    FrameLayout flFragment;
    @BindView(R.id.iv1)
    ImageView iv1;
    @BindView(R.id.tv1)
    TextView tv1;
    @BindView(R.id.ll_tab1)
    LinearLayout llTab1;
    @BindView(R.id.iv2)
    ImageView iv2;
    @BindView(R.id.tv2)
    TextView tv2;
    @BindView(R.id.ll_tab2)
    LinearLayout llTab2;
    @BindView(R.id.iv3)
    ImageView iv3;
    @BindView(R.id.tv3)
    TextView tv3;
    @BindView(R.id.ll_tab3)
    LinearLayout llTab3;
    Unbinder unbinder;
    Fragment1 fragment1;
    Fragment2 fragment2;
    Fragment3 fragment3;
    private Fragment fragment_now = null;
    private List<ImageView> iv_list;
    private List<TextView> tv_list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation1);
        unbinder = ButterKnife.bind(this);
        inint();
    }
    private void inint() {
        iv_list = new ArrayList<>();
        tv_list = new ArrayList<>();
        iv_list.add(iv1);
        iv_list.add(iv2);
        iv_list.add(iv3);
        tv_list.add(tv1);
        tv_list.add(tv2);
        tv_list.add(tv3);
        changePageSelect(0);
        changePageFragment(R.id.ll_tab1);
    }
    @OnClick({R.id.iv1, R.id.ll_tab1, R.id.iv2, R.id.ll_tab2, R.id.iv3, R.id.ll_tab3})
    public void onViewClicked(View view) {
        changePageFragment(view.getId());
    }

    /**
     * 选中的tab 和 没有选中的tab 的图标和字体颜色
     *
     * @param index
     */
    public void changePageSelect(int index) {
        for (int i = 0; i < iv_list.size(); i++) {
            if (index == i) {
                iv_list.get(i).setEnabled(false);
                tv_list.get(i).setTextColor(getResources().getColor(R.color.colorLightRed));
            } else {
                iv_list.get(i).setEnabled(true);
                tv_list.get(i).setTextColor(getResources().getColor(R.color.colorTextGrey));
            }
        }
    }
    /**
     * 当点击导航栏时改变 fragment
     *
     * @param id
     */
    public void changePageFragment(int id) {
        switch (id) {
            case R.id.ll_tab1:
            case R.id.iv1:
                if (fragment1 == null) {//减少new fragmnet,避免不必要的内存消耗
                    fragment1 = Fragment1.newInstance();
                }
                changePageSelect(0);
                switchFragment(fragment_now, fragment1);
                break;
            case R.id.ll_tab2:
            case R.id.iv2:
                if (fragment2 == null) {
                    fragment2 = Fragment2.newInstance();
                }
                changePageSelect(1);
                switchFragment(fragment_now, fragment2);
                break;
            case R.id.ll_tab3:
            case R.id.iv3:
                if (fragment3 == null) {
                    fragment3 = Fragment3.newInstance();
                }
                changePageSelect(2);
                switchFragment(fragment_now, fragment3);
                break;
        }
    }
    /**
     * 隐藏显示fragment
     *
     * @param from 需要隐藏的fragment
     * @param to   需要显示的fragment
     */
    public void switchFragment(Fragment from, Fragment to) {
        if (to == null)
            return;
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!to.isAdded()) {
            if (from == null) {
                transaction.add(R.id.fl_fragment, to).show(to).commit();
            } else {
                // 隐藏当前的fragmentadd下一个fragmentActivity中并显示
                transaction.hide(from).add(R.id.fl_fragment, to).show(to).commitAllowingStateLoss();
            }
        } else {
            // 隐藏当前的fragment,显示下一个
            transaction.hide(from).show(to).commit();
        }
        fragment_now = to;

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除绑定
        unbinder.unbind();
    }
}


首先 build.gradle 添加


implementation 'com.jakewharton:butterknife:8.8.1'


用 butterKnife找到控件并设置点击事件,并用List<ImageView>  iv_list 将 ImageView add,

List<TextView> tv_list将 TextView add,


然后是fragment


Fragment1 代码 :


public class Fragment1 extends Fragment {

    public Fragment1() {
    }

    public static Fragment1 newInstance() {
        Fragment1 fragment = new Fragment1();
        return fragment;
    }

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

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

}


Fragment1布局  fragment_1:


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第一个fragment"
        android:layout_gravity="center"/>
</FrameLayout>

Fragment2, Fragment3 及其布局 依葫芦画瓢


然后写changePageSelect() 改变 选中的tab 和 没有选中的tab 的图标和字体颜色


public void changePageSelect(int index) {
    for (int i = 0; i < iv_list.size(); i++) {
        if (index == i) {
            iv_list.get(i).setEnabled(false);
            tv_list.get(i).setTextColor(getResources().getColor(R.color.colorLightRed));
        } else {
            iv_list.get(i).setEnabled(true);
            tv_list.get(i).setTextColor(getResources().getColor(R.color.colorTextGrey));
        }
    }
}


然后写switchFragment() 添加 ,显示, 隐藏 frament


public void switchFragment(Fragment from, Fragment to) {
    if (to == null)
        return;
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    if (!to.isAdded()) {
        if (from == null) {
            transaction.add(R.id.fl_fragment, to).show(to).commit();
        } else {
            // 隐藏当前的fragmentadd下一个fragmentActivity中并显示
             transaction.hide(from).add(R.id.fl_fragment,to).show(to)
             .commitAllowingStateLoss();
        }
    } else {
        // 隐藏当前的fragment,显示下一个
        transaction.hide(from).show(to).commit();
    }
    fragment_now = to;

}

此方法传入 Fragment form , Fragment to 。 首先判断 Fragment to 是否为 null,

    Fragment to为 null 则 return;Fragment to不为 null  往下走得到  FragmnetTranscation transcation。

    然后判断 Fragment to 是否添加到Activity, 

                    Fragment to 没有添加到了 Activity 则 判断 Fragment  form 是否为 null ,

                             Fragment form 为 null 则将  Fragment to 添加到Activity 中 并显示;

                             Fragment form 不为null 则 隐藏Fragment form,将Fragment to 添加到Activity 中 并显示;

                    Fragment to 添加到了 Activity  则隐藏Fragment form, 显示 Fragment to;

     最后将 Fragment to 赋值给 Fragment fragment_now


最后写changepageFragment() , 调用 changePageSelect() 与 switchFragment() 并给点击事件调用


public void changePageFragment(int id) {
    switch (id) {
        case R.id.ll_tab1:
        case R.id.iv1:
            if (fragment1 == null) {//减少new fragmnet,避免不必要的内存消耗
                fragment1 = Fragment1.newInstance();
            }
            changePageSelect(0);
            switchFragment(fragment_now, fragment1);
            break;
        case R.id.ll_tab2:
        case R.id.iv2:
            if (fragment2 == null) {
                fragment2 = Fragment2.newInstance();
            }
            changePageSelect(1);
            switchFragment(fragment_now, fragment2);
            break;
        case R.id.ll_tab3:
        case R.id.iv3:
            if (fragment3 == null) {
                fragment3 = Fragment3.newInstance();
            }
            changePageSelect(2);
            switchFragment(fragment_now, fragment3);
            break;
    }
}
 
@OnClick({R.id.iv1, R.id.ll_tab1, R.id.iv2, R.id.ll_tab2, R.id.iv3, R.id.ll_tab3})
public void onViewClicked(View view) {
    changePageFragment(view.getId());

}

 changepageFragment() 的妙处就是调用 switchFragment() 第一个参数 传了 Fragment fragment_now,

 这样 Fragment fragment_now() 与  switchFragment() 就通过 Fragment fragment_now 紧密联系起来



到此底部导航栏 LinearLyaout + ImageView + TextView 的写法到此就介绍完毕了!



                                        二.  BotoomNavigationView

 

绝多数app都用底部导航栏,所以 google 爸爸为了方便开发者在 Design Support Library中添加了 BotoomNavigationView控件,我们就不用苦逼的LinearLyaout + ImageView + TextView 布局写法了!


首先 build.gradle 添加

   

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

然后布局:


<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >
    <FrameLayout
        android:id="@+id/fl_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomNavigationView"
        />
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@drawable/selector_bottom_navigation"
        app:itemTextColor="@drawable/selector_bottom_navigation"
        app:menu="@menu/menu_bottom_navigation"
        />
</android.support.percent.PercentRelativeLayout>
app:itemIconTint 为图标颜色变化 , app:itemtextColor 为为文字颜色变化



然后在drawable下 创建 selector_bottom_navigantion


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:color="@color/colorLightGreen"  />
    <item  android:state_checked="false" android:color="@color/colorTextGrey" />
</selector>


然后在res 创建 menu文件夹,并在下面创建 menu_bottom_navigation


<?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/item_1"
        android:icon="@mipmap/img_1"
        android:title="@string/menu_bottom_navigation1"
        />
    <item
        android:id="@+id/item_2"
        android:icon="@mipmap/img_2"
        android:title="@string/menu_bottom_navigation2"
      />

    <item
        android:id="@+id/item_3"
        android:icon="@mipmap/img_3"
        android:title="@string/menu_bottom_navigation3"
        />
</menu>


最后Activity代码



public class BottomNavigationActivity2 extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener{

    @BindView(R.id.fl_fragment)
    FrameLayout flFragment;
    @BindView(R.id.bottomNavigationView)
    BottomNavigationView bottomNavigationView;
    Unbinder unbinder;
    Fragment1 fragment1;
    Fragment2 fragment2;
    Fragment3 fragment3;
    private Fragment fragment_now = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation2);
        unbinder = ButterKnife.bind(this);
        inint();
    }

    @SuppressLint("NewApi")
    private void inint() {
        bottomNavigationView.setOnNavigationItemSelectedListener(this);//设置 NavigationItemSelected 事件监听
        BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);//改变 BottomNavigationView 默认的效果
        //选中第一个item,对应第一个fragment
        bottomNavigationView.setSelectedItemId(R.id.item_1);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
    }
    //NavigationItemSelected 事件监听
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        changePageFragment(item.getItemId());
        return true;
    }
    /**
     * 当点击导航栏时改变fragment
     *
     * @param id
     */
    public void changePageFragment(int id) {
        switch (id) {
            case R.id.item_1:
                if (fragment1 == null) { //减少new fragmnet,避免不必要的内存消耗
                    fragment1 = Fragment1.newInstance();
                }
                    switchFragment(fragment_now, fragment1);
                break;
            case R.id.item_2:
                if (fragment2 == null) {
                    fragment2 = Fragment2.newInstance();
                }
                    switchFragment(fragment_now, fragment2);
                break;
            case R.id.item_3:
                if (fragment3 == null) {
                    fragment3 = Fragment3.newInstance();
                }
                    switchFragment(fragment_now, fragment3);
                break;
        }
    }
    /**
     * 隐藏显示fragment
     *
     * @param from 需要隐藏的fragment
     * @param to   需要显示的fragment
     */
    public void switchFragment(Fragment from, Fragment to) {
        if (to == null)
            return;
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!to.isAdded()) {
            if (from == null) {
                transaction.add(R.id.fl_fragment, to).show(to).commit();
            } else {
                // 隐藏当前的fragmentadd下一个fragmentActivity                transaction.hide(from).add(R.id.fl_fragment, to).commitAllowingStateLoss();
            }
        } else {
            // 隐藏当前的fragment,显示下一个
            transaction.hide(from).show(to).commit();
        }
        fragment_now = to;
    }
}


switchFragment() 与第一种写法一致; changePageFragment() 与第一种写法区别在于没有changePageSelect(), 因为BottomNavigationView 已经在布局app:itemIconTint 与 app:itemtextColor做了这个事。


最后设置 BottomNavigationView 的 item 选择事件监听调用changePageFragment()



public class BottomNavigationActivity2 extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener
bottomNavigationView.setOnNavigationItemSelectedListener(this);

 @Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    changePageFragment(item.getItemId());
    return true;
}



 如果BottomNavigationView 的 item >3 则会出现 位移效果,而我们不要位移效果怎么办?


BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);

public class BottomNavigationViewHelper {

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @SuppressLint("RestrictedApi")
    public static void disableShiftMode(BottomNavigationView navigationView) {

        BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.getChildAt(0);
        try {
            // 利用反射,改变 item  mShiftingMode 的值 ,从而改变 BottomNavigationView 默认的效果
            Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
            shiftingMode.setAccessible(true);
            shiftingMode.setBoolean(menuView, false);
            shiftingMode.setAccessible(false);

            for (int i = 0; i < menuView.getChildCount(); i++) {
                BottomNavigationItemView itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                itemView.setShiftingMode(false);
                itemView.setChecked(itemView.getItemData().isChecked());
            }

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


有人会问:这样为什么可以改变默认大小?

在此先简单分析(以后分析BottomNavigationView源码 ):

           通过 BottomNavigationView 找到 BottomNavigationMenuView , 在BottomNavigationMenuView 看到

            

private boolean mShiftingMode = true;
   
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int width = MeasureSpec.getSize(widthMeasureSpec);
    final int count = getChildCount();

    final int heightSpec = MeasureSpec.makeMeasureSpec(mItemHeight, MeasureSpec.EXACTLY);

    if (mShiftingMode) {
        final int inactiveCount = count - 1;
        final int activeMaxAvailable = width - inactiveCount * mInactiveItemMinWidth;
        final int activeWidth = Math.min(activeMaxAvailable, mActiveItemMaxWidth);
        final int inactiveMaxAvailable = (width - activeWidth) / inactiveCount;
        final int inactiveWidth = Math.min(inactiveMaxAvailable, mInactiveItemMaxWidth);
        int extra = width - activeWidth - inactiveWidth * inactiveCount;
        for (int i = 0; i < count; i++) {
            mTempChildWidths[i] = (i == mSelectedItemPosition) ? activeWidth : inactiveWidth;
            if (extra > 0) {
                mTempChildWidths[i]++;
                extra--;
            }
        }
    } else {
        final int maxAvailable = width / (count == 0 ? 1 : count);
        final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
        int extra = width - childWidth * count;
        for (int i = 0; i < count; i++) {
            mTempChildWidths[i] = childWidth;
            if (extra > 0) {
                mTempChildWidths[i]++;
                extra--;
            }
        }
    }

    int totalWidth = 0;
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() == GONE) {
            continue;
        }
        child.measure(MeasureSpec.makeMeasureSpec(mTempChildWidths[i], MeasureSpec.EXACTLY),
                heightSpec);
        ViewGroup.LayoutParams params = child.getLayoutParams();
        params.width = child.getMeasuredWidth();
        totalWidth += child.getMeasuredWidth();
    }
    setMeasuredDimension(
            View.resolveSizeAndState(totalWidth,
                    MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), 0),
            View.resolveSizeAndState(mItemHeight, heightSpec, 0));
}

BottomNavigationMenuView 通过 mShiftingMode 为标识 ,  measure 它的各子view 的宽高,而宽在不断变化,从而产生位移变化。


有人会问:为什么是item >3 则会出现 位移效果

因为在BottomNavigationMenuView 的 buildMenuView()中的mShiftingMode = mMenu.size() > 3

public void buildMenuView() {
    removeAllViews();
    if (mButtons != null) {
        for (BottomNavigationItemView item : mButtons) {
            mItemPool.release(item);
        }
    }
    if (mMenu.size() == 0) {
        mSelectedItemId = 0;
        mSelectedItemPosition = 0;
        mButtons = null;
        return;
    }
    mButtons = new BottomNavigationItemView[mMenu.size()];
    mShiftingMode = mMenu.size() > 3;
    for (int i = 0; i < mMenu.size(); i++) {
        mPresenter.setUpdateSuspended(true);
        mMenu.getItem(i).setCheckable(true);
        mPresenter.setUpdateSuspended(false);
        BottomNavigationItemView child = getNewItem();
        mButtons[i] = child;
        child.setIconTintList(mItemIconTint);
        child.setTextColor(mItemTextColor);
        child.setItemBackground(mItemBackgroundRes);
        child.setShiftingMode(mShiftingMode);
        child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
        child.setItemPosition(i);
        child.setOnClickListener(mOnClickListener);
        addView(child);
    }
    mSelectedItemPosition = Math.min(mMenu.size() - 1, mSelectedItemPosition);
    mMenu.getItem(mSelectedItemPosition).setChecked(true);
}


如果你想有滑动切换效果,可以 添加 ViewPager


build.gradle 添加

implementation 'com.android.support:support-v4:27.0.1'

布局修改为


<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >
    <!--<FrameLayout-->
        <!--android:id="@+id/fl_fragment"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"-->
        <!--android:layout_above="@+id/bottomNavigationView"-->
        <!--/>-->
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottomNavigationView" />
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        app:itemIconTint="@drawable/selector_bottom_navigation"
        app:itemTextColor="@drawable/selector_bottom_navigation"
        app:menu="@menu/menu_bottom_navigation"
        />
</android.support.percent.PercentRelativeLayout>


Activity代码修改为


public class BottomNavigationActivity2 extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener,ViewPager.OnPageChangeListener,ViewPager.OnTouchListener{
    @BindView(R.id.bottomNavigationView)
    BottomNavigationView bottomNavigationView;
    @BindView(R.id.viewpager)
    ViewPager viewPager;
    Unbinder unbinder;
    Fragment1 fragment1;
    Fragment2 fragment2;
    Fragment3 fragment3;
    BottomnavigationViewPagerAdapter pagerAdapter;
    List<Fragment> fragments;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation2);
        unbinder = ButterKnife.bind(this);
        inint();
    }

    @SuppressLint("NewApi")
    private void inint() {
        fragments = new ArrayList<>();
        fragment1 = Fragment1.newInstance();
        fragment2 = Fragment2.newInstance();
        fragment3 = Fragment3.newInstance();
        if(!fragments.contains(fragment1)){
            fragments.add(fragment1);
        }
        if(!fragments.contains(fragment2)){
            fragments.add(fragment2);
        }
        if(!fragments.contains(fragment3)){
            fragments.add(fragment3);
        }
        bottomNavigationView.setOnNavigationItemSelectedListener(this);//设置 NavigationItemSelected 事件监听
        BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);//改变 BottomNavigationView 默认的效果
        //选中第一个item,对应第一个fragment
        bottomNavigationView.setSelectedItemId(R.id.item_1);
        pagerAdapter = new BottomnavigationViewPagerAdapter(getSupportFragmentManager(),fragments);
        viewPager.setAdapter(pagerAdapter);
        viewPager.addOnPageChangeListener(this);
        // 如果想禁止滑动,可以把下面的代码取消注释
//        viewPager.setOnTouchListener(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbinder.unbind();
    }

    //NavigationItemSelected 事件监听
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case  R.id.item_1:
                viewPager.setCurrentItem(0);
                break;
            case  R.id.item_2:
                viewPager.setCurrentItem(1);
                break;
            case  R.id.item_3:
                viewPager.setCurrentItem(3);
                break;
        }
        return true;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }
    @Override
    public void onPageSelected(int position) {
        bottomNavigationView.getMenu().getItem(position).setChecked(true);
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }
}


最后 注意BottomNavigationView item 范围 1~5 个 (早期版本是 3~5 个)


public final class BottomNavigationMenu extends MenuBuilder {
    public static final int MAX_ITEM_COUNT = 5;

   ..............

     @Override
    protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
        if (size() + 1 > MAX_ITEM_COUNT) {
            throw new IllegalArgumentException(
                    "Maximum number of items supported by BottomNavigationView is " + MAX_ITEM_COUNT
                            + ". Limit can be checked with BottomNavigationView#getMaxItemCount()");
        }
        stopDispatchingItemsChanged();
        final MenuItem item = super.addInternal(group, id, categoryOrder, title);
        if (item instanceof MenuItemImpl) {
            ((MenuItemImpl) item).setExclusiveCheckable(true);
        }
        startDispatchingItemsChanged();
        return item;
    }
}


到此底部导航栏 BottomNavigationView 的用法到此就介绍完毕了!


最后效果



 


代码地址:https://github.com/ccWenTian/notes,   如果觉得还行,请点个赞。







         

2016-06-01 11:49:40 jiabailong 阅读数 1220

此项目为一个图层导航工程,图层构成有精灵对象,碰撞监测点,及位置示意图。操作方式采用游戏式风格,支持碰撞监测点的定义添加,图层图层可拖动。工程地址:https://github.com/jiabailong/android-spriteLayer希望大家多提提意见。      

    

2017-07-30 13:58:59 hellolengyue 阅读数 2089

底部导航栏的实现也不难,就是下边是几个Tab切换,上边一般是一个FrameLayout,然后FrameLayout中切换fragment。

网上有不少关于Android底部导航栏的文章,不过好像都只是关于下边Tab切的,没有实现Tab与fragment的联动,用的时候还要自己手写这部分代码,对我这个比较懒(据说,懒是程序员的一种美德^_^#)得程序员来说,这是不能忍的。

下边就来说说我的BottomTabBar吧


这就是我以前封装过的BottomTabBar的效果图,现在就从这张效果图开始,先分析一下,都需要设置那些参数吧。

1、BottomTabBar的整体背景

虽然一般这里都是用白色或者接近白色的浅色调作为背景,但我们也不能给他固定死,要提供这样的一个方法,让使用者可以把背景设置成任何的颜色。

2、图片

这里不仅要传入一个图片,还要做图片做一些设置:

  • 图片的宽高尺寸(这个也需要对外设置一个方法)
  • 图片得设置居中,这个直接固定写死就好了,我见过的应用都是设置居中的,没见过别的情况,个人感觉,不设置居中也不美观啊
3、 文字

与图片类似,文字也需要做一些设置:

  • 文字的大小
  • 文字也是需要设置居中的,也像图片一样固定写死
4、 颜色

文字和图片的颜色都是只有两种,一种是选中状态下的,一种是未选中的,我们可以在这里统一设置,提供一个方法就可以了

5、边距

这里需要设置三个地方的边距:

  • Top边距,也就是图片与上边分割线的距离
  • middle边距,也就是图片与文字的距离
  • Bottom边距,也就是文字与底部的距离
6、分割线

上边说到了,图片上边需要设置分割线,当然,这只是部分使用者需要设置的,所以我们需要提供一个方法,用来设置是否显示分割线。此外还要设置分割线的高度以及其背景颜色

7、fragment

我这个BottomTabBar既然是要与fragment联动的,所以必须要传入一个fragment

大体的参数就是需要这些了,下面上代码:

GitHub代码连接


用法也简单,就像标题说的几行代码就可以搞定:

引用方式:

compile 'com.hjm:BottomTabBar:1.0.0'

1. 首先是XML文件代码:
<com.hjm.bottomtabbar.BottomTabBar
     android:id="@+id/bottom_tab_bar"
     android:layout_width="match_parent"
     android:layout_height="match_parent"/>

简单吧,就这么一个控件就可以了。
当然,你要是想进行一些属性设置的话,需要加上命名空间
xmlns:hjm="http://schemas.android.com/apk/res-auto"

下面就开始详细的解释一下每个参数的含义以及用法:

参数名 涵义
tab_bar_background BottomTabBar的整体背景颜色
tab_img_width 图片宽度
tab_img_height 图片高度
tab_font_size 文字尺寸
tab_padding_top 上边距
tab_img_font_padding 图片文字间隔
tab_padding_bottom 下边距
tab_isshow_divider 是否显示分割线
tab_divider_height 分割线高度
tab_divider_background 分割线背景
tab_selected_color 选中的颜色
tab_unselected_color 未选中的颜色
这些参数可以指接在XML文件里设置
<com.hjm.bottomtabbar.BottomTabBar
        android:id="@+id/bottom_tab_bar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        hjm:tab_divider_background="#FF0000"
        hjm:tab_divider_height="5dp"
        hjm:tab_font_size="6sp"
        hjm:tab_img_font_padding="0dp"
        hjm:tab_img_height="30dp"
        hjm:tab_img_width="30dp"
        hjm:tab_isshow_divider="true"
        hjm:tab_padding_bottom="5dp"
        hjm:tab_padding_top="8dp"
        hjm:tab_selected_color="#000000"
        hjm:tab_unselected_color="@color/colorPrimary" />
2. Activity文件代码:
mBottomTabBar = (BottomTabBar) findViewById(R.id.bottom_tab_bar);
mBottomTabBar.init(getSupportFragmentManager())
     .addTabItem("第一项", R.mipmap.ic_launcher, OneFragment.class)
     .addTabItem("第二项", R.mipmap.ic_launcher, TwoFragment.class)
     .addTabItem("第三项", R.mipmap.ic_launcher, ThreeFragment.class)
     .addTabItem("第四项", R.mipmap.ic_launcher, FourFragment.class);

也很简单,是吧。

这里简单的提一句,这个init ( getSupportFragmentManager() )方法一定要第一个调用,没有这个初始化,后边什么也做不了。

或许大家也看出来了,这个init()方法里,我们需要传入一个FragmentManager,而且还是V4包下的,所以,在使用Activity的时候需要注意一下。

下边是一些方法的使用,都加了注释了

    /**
     * 设置图片的尺寸
     * <p>
     * 此方法必须在addTabItem()之前调用
     *
     * @param width  宽度 px
     * @param height 高度 px
     * @return
     */
    setImgSize(float width, float height)

    /**
     * 设置文字的尺寸
     * <p>
     * 此方法必须在addTabItem()之前调用
     *
     * @param textSize 文字的尺寸 sp
     * @return
     */
    setFontSize(float textSize)

    /**
     * 设置Tab的padding值
     * <p>
     * 此方法必须在addTabItem()之前调用
     *
     * @param top    上边距 px
     * @param middle 文字图片的距离 px
     * @param bottom 下边距 px
     * @return
     */
    setTabPadding(float top, float middle, float bottom)

    /**
     * 设置选中未选中的颜色
     * <p>
     * 此方法必须在addTabItem()之前调用
     *
     * @param selectColor   选中的颜色
     * @param unSelectColor 未选中的颜色
     * @return
     */
    setChangeColor(@ColorInt int selectColor, @ColorInt int unSelectColor)

    /**
     * 设置BottomTabBar的整体背景
     *
     * @param color 背景颜色
     * @return
     */
    setTabBarBackgroundColor(@ColorInt int color) 

    /**
     * 是否显示分割线
     *
     * @param isShowDivider
     * @return
     */
    isShowDivider(boolean isShowDivider)

    /**
     * 设置分割线的高度
     *
     * @param height
     * @return
     */
    setDividerHeight(float height)

    /**
     * 设置分割线的颜色
     *
     * @param color
     * @return
     */
    setDividerColor(@ColorInt int color) 

   /**
     * 添加TabItem
     *
     * @param name          文字
     * @param drawable      图片
     * @param fragmentClass fragment
     * @return
     */
    addTabItem(String name, Drawable drawable, Class fragmentClass)

值得注意的是前四个方法必须在addTabItem()之前调用,如果放在addTabItem()之后调用的话,就没有任何的效果了。

正确用法如下:
mBottomTabBar.init(getSupportFragmentManager())
     .setImgSize(50,50)
     .setFontSize(8)
     .setTabPadding(4,6,10)
     .setChangeColor(Color.DKGRAY,Color.RED)
     .addTabItem("第一项", R.mipmap.ic_launcher, OneFragment.class)
     .addTabItem("第二项", R.mipmap.ic_launcher, TwoFragment.class)
     .addTabItem("第三项", R.mipmap.ic_launcher, ThreeFragment.class)
     .addTabItem("第四项", R.mipmap.ic_launcher, FourFragment.class)
     .setTabBarBackgroundColor(Color.WHITE)
     .isShowDivider(false);