Android底部导航栏的主界面实现

0
回复
34
查看
打印 上一主题 下一主题
[复制链接]

4

主题

8

帖子

236

安币

攻城狮

Rank: 3Rank: 3

楼主
发表于 昨天?14:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
在主流app中,应用的主界面都是底部含有多个标签的导航栏,点击可以切换到相应的界面,就拿腾讯的QQ与微信来说,看起来是这样的

原理

在很久以前,可以通过TabActivity实现相关功能,自从Fragment出来后,就被抛弃了。

原理也很简单

1、底部菜单通过自定义RadioGroup实现,通过setOnCheckedChangeListener监听切换内容。

2、内容切换,可以使用ViewPager(可以实现直接滑动切换),TabHost,FragmentManager来实现。

接下来将描述下其实现过程。
1.首先是分析界面,底部导航栏我们可以用一个占满屏幕宽度、包裹着数个标签TextView、方向为横向horizontal的线性布局LinearLayout。上方则是一个占满剩余空间的FrameLayout。
activity_main.xml
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=".MainActivity"
android:orientation="vertical">
<>
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<>
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/colorPrimaryDark"
/>
<>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<>
android:id="@+id/main_home"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:text="首页"
/>
<>
android:layout_width="1px"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
/>
<>
android:id="@+id/main_game"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:text="游戏"
/>
<>
android:layout_width="1px"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
/>
<>
android:id="@+id/main_video"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:text="视频"
/>
<>
android:layout_width="1px"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
/>
<>
android:id="@+id/main_mine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="20dp"
android:text="我的"
/>
2.四个标签对应四个Fragment,我们新建四个Fragment,布局放个TextView用于区分即可。
private HomeFragment homeFragment;
private GameFragment gameFragment;
private VideoFragment videoFragment;
private MineFragment mineFragment;
3.打开MainActivity,首先初始化控件
private void initView() {
home= findViewById(R.id.main_home);
game= findViewById(R.id.main_game);
video= findViewById(R.id.main_video);
mine= findViewById(R.id.main_mine);
layout= findViewById(R.id.main_layout);
home.setOnClickListener(this);
game.setOnClickListener(this);
video.setOnClickListener(this);
mine.setOnClickListener(this);
}
onClick点击事件放在后面处理
3.初始化四个fragment
我们初衷是让fragment加载一次后,重复点击或者切换回来都不会再加载以耗费资源,这里常见的处理方式有viewpager的懒加载和fragmenthideshow,这里我们讲解后者的实现方式。
private void initFragment() {
FragmentTransaction transaction= getSupportFragmentManager().beginTransaction();
if (homeFragment!= null&& homeFragment.isAdded()){
transaction.remove(homeFragment);
}
if (gameFragment!= null&& gameFragment.isAdded()){
transaction.remove(gameFragment);
}
if (videoFragment!= null&& videoFragment.isAdded()){
transaction.remove(videoFragment);
}
if (mineFragment!= null&& mineFragment.isAdded()){
transaction.remove(mineFragment);
}
transaction.commitAllowingStateLoss();
homeFragment= null;
gameFragment= null;
videoFragment= null;
mineFragment= null;
home.performClick();
}
我们来逐行分析代码
首先开启一个事务
FragmentTransaction transaction= getSupportFragmentManager().beginTransaction();
进行Fragment的非空处理
if (homeFragment!= null&& homeFragment.isAdded()){
transaction.remove(homeFragment);
}
if (gameFragment!= null&& gameFragment.isAdded()){
transaction.remove(gameFragment);
}
if (videoFragment!= null&& videoFragment.isAdded()){
transaction.remove(videoFragment);
}
if (mineFragment!= null&& mineFragment.isAdded()){
transaction.remove(mineFragment);
}
transaction.commitAllowingStateLoss();
将所有fragment设为null,自动执行homefragment的点击事件,即默认显示HomeFragment
homeFragment= null;
gameFragment= null;
videoFragment= null;
mineFragment= null;
home.performClick();
4.回到四个底部标签的点击事件,我们执行自定义的switchContent方法,将当前点击标签的view作为参数传进去
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.main_home:
switchContent(home);
break;
case R.id.main_game:
switchContent(game);
break;
case R.id.main_video:
switchContent(video);
break;
case R.id.main_mine:
switchContent(mine);
break;
}
}
5.定位到switchContent方法
新建一个fragment对象,当点击某个标签时,如果当前fragment对象为空,就生成一个对应fragment的对象实例
public void switchContent(View view){
Fragment fragment;
if (view== home){
if (homeFragment== null){
homeFragment= new HomeFragment();
}
fragment= homeFragment;
}else if (view== game){
if (gameFragment== null){
gameFragment= new GameFragment();
}
fragment= gameFragment;
}else if (view== video){
if (videoFragment== null){
videoFragment= new VideoFragment();
}
fragment= videoFragment;
}else if (view== mine){
if (mineFragment== null){
mineFragment= new MineFragment();
}
fragment= mineFragment;
}else {
return;
}
生成对象后,我们就可以进行fragment的添加显示工作了。
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (mContent == null) {
transaction.add(layout.getId(), fragment).commit();
mContent = fragment;
}
if (mContent != fragment) {
if (!fragment.isAdded()) {
transaction.hide(mContent).add(layout.getId(), fragment).commitAllowingStateLoss();
} else {
transaction.hide(mContent).show(fragment).commitAllowingStateLoss();
}
mContent = fragment;
}
home.setSelected(false);
home.setSelected(false);
home.setSelected(false);
home.setSelected(false);
view.setSelected(true);
分析这段代码,我们主要是用当前碎片mContent和上个碎片fragment做比较,这样用来判断底部导航栏是否点击进行了切换,首先当应用打开时,因为我们前面调用了第一个标签自动点击方法。
home.performClick();
看流程,因为此时mContent还为null,所以走这段代码
if (mContent == null) {
transaction.add(layout.getId(), fragment).commit();
mContent = fragment;
}
此时fragment即为HomeFragment对象,页面将显示HomeFragment,并将该对象赋给mContent
此时HomeFragment生命周期如下,从Attach()走到onResume(),一切正常。
接下来,点击第二个标签,fragmentgameFragmentmContenthomeFragment。两者不等,走这段方法。
if (mContent != fragment) {
if (!fragment.isAdded()) {
transaction.hide(mContent).add(layout.getId(), fragment).commitAllowingStateLoss();
} else {
transaction.hide(mContent).show(fragment).commitAllowingStateLoss();
}
mContent = fragment;
}
分析代码,GameFragment因为还没被加载过,所以先走
transaction.hide(mContent).add(layout.getId(), fragment).commitAllowingStateLoss();
即隐藏掉mContentHomeFragmentfunction(){? ?//外汇返佣www.fx61.com在将GameFragment加载显示出来。
这时看GameFragment的生命周期,一切正常。
这时我们再点击回HomeFragment,此时fragment为HomeFragment,mContent为GameFragment,同时HomeFragment因为被add过,所以走
transaction.hide(mContent).show(fragment).commitAllowingStateLoss();
这条语句指隐藏fragment同时不必加载add已经加载过的fragment,直接show就可以,commitAllowingStateLoss方法与commit方法作用类似,更适用这种频繁切换页面下的提交工作,避免crash。
同时打开日志,发现HomeFragment并没有被销毁重载,这样就达到了我们不想切换频繁加载的目的。
RadioGroup + ViewPager
? ? 这是一种比较常见了的,下面 4 个 tab 的导航按钮,可以切换不同的页面,这里页面使用了 ViewPager + Fragment 的组合,实现了滑动的页面效果,也可以不使用 ViewPager,这个根据产品的定义来使用即可。
1. 布局文件
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? tools:context=".style2.Style2Activity">
? ? <>
? ?? ???android:id="@+id/fragment_vp"
? ?? ???android:layout_width="match_parent"
? ?? ???android:layout_height="match_parent"
? ?? ???android:layout_above="@+id/tabs_rg" />
? ? <>
? ?? ???android:id="@+id/tabs_rg"
? ?? ???android:layout_width="match_parent"
? ?? ???android:layout_height="56dp"
? ?? ???android:layout_alignParentBottom="true"
? ?? ???android:background="#dcdcdc"
? ?? ???android:orientation="horizontal">
? ?? ???<>
? ?? ?? ?? ?android:id="@+id/today_tab"
? ?? ?? ?? ?style="@style/Custom.TabRadioButton"
? ?? ?? ?? ?android:checked="true"
? ?? ?? ?? ?android:drawableTop="@drawable/tab_sign_selector"
? ?? ?? ?? ?android:text="今日" />
? ?? ???<>
? ?? ?? ?? ?android:id="@+id/record_tab"
? ?? ?? ?? ?style="@style/Custom.TabRadioButton"
? ?? ?? ?? ?android:drawableTop="@drawable/tab_record_selector"
? ?? ?? ?? ?android:text="记录" />
? ?? ???<>
? ?? ?? ?? ?android:id="@+id/contact_tab"
? ?? ?? ?? ?style="@style/Custom.TabRadioButton"
? ?? ?? ?? ?android:drawableTop="@drawable/tab_contact_selector"
? ?? ?? ?? ?android:text="通讯录" />
? ?? ???<>
? ?? ?? ?? ?android:id="@+id/settings_tab"
? ?? ?? ?? ?style="@style/Custom.TabRadioButton"
? ?? ?? ?? ?android:drawableTop="@drawable/tab_setting_selector"
? ?? ?? ?? ?android:text="设置" />
? ?
2. Activity 类
public class Style2Activity extends AppCompatActivity {
? ? private ViewPager mViewPager;
? ? private RadioGroup mTabRadioGroup;
? ? private List mFragments;
? ? private FragmentPagerAdapter mAdapter;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ?? ???super.onCreate(savedInstanceState);
? ?? ???setContentView(R.layout.activity_style2);
? ?? ???initView();
? ? }
? ? private void initView() {
? ?? ???// find view
? ?? ???mViewPager = findViewById(R.id.fragment_vp);
? ?? ???mTabRadioGroup = findViewById(R.id.tabs_rg);
? ?? ???// init fragment
? ?? ???mFragments = new ArrayList<>(4);
? ?? ???mFragments.add(BlankFragment.newInstance("今日"));
? ?? ???mFragments.add(BlankFragment.newInstance("记录"));
? ?? ???mFragments.add(BlankFragment.newInstance("通讯录"));
? ?? ???mFragments.add(BlankFragment.newInstance("设置"));
? ?? ???// init view pager
? ?? ???mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), mFragments);
? ?? ???mViewPager.setAdapter(mAdapter);
? ?? ???// register listener
? ?? ???mViewPager.addOnPageChangeListener(mPageChangeListener);
? ?? ???mTabRadioGroup.setOnCheckedChangeListener(mOnCheckedChangeListener);
? ? }
? ? @Override
? ? protected void onDestroy() {
? ?? ???super.onDestroy();
? ?? ???mViewPager.removeOnPageChangeListener(mPageChangeListener);
? ? }
? ? private ViewPager.OnPageChangeListener mPageChangeListener = new ViewPager.OnPageChangeListener() {
? ?? ???@Override
? ?? ???public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
? ?? ???}
? ?? ???@Override
? ?? ???public void onPageSelected(int position) {
? ?? ?? ?? ?RadioButton radioButton = (RadioButton) mTabRadioGroup.getChildAt(position);
? ?? ?? ?? ?radioButton.setChecked(true);
? ?? ???}
? ?? ???@Override
? ?? ???public void onPageScrollStateChanged(int state) {
? ?? ???}
? ? };
? ? private RadioGroup.OnCheckedChangeListener mOnCheckedChangeListener = new RadioGroup.OnCheckedChangeListener() {
? ?? ???@Override
? ?? ???public void onCheckedChanged(RadioGroup group, int checkedId) {
? ?? ?? ?? ?for (int i = 0; i < group.getChildCount(); i++) {
? ?? ?? ?? ?? ? if (group.getChildAt(i).getId() == checkedId) {
? ?? ?? ?? ?? ?? ???mViewPager.setCurrentItem(i);
? ?? ?? ?? ?? ?? ???return;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?}
? ?? ???}
? ? };
? ? private class MyFragmentPagerAdapter extends FragmentPagerAdapter {
? ?? ?private List mList;
? ?? ???public MyFragmentPagerAdapter(FragmentManager fm, List list) {
? ?? ?? ?? ?super(fm);
? ?? ?? ?? ?this.mList = list;
? ?? ???}
? ?? ???@Override
? ?? ???public Fragment getItem(int position) {
? ?? ?? ?? ?return this.mList == null ? null : this.mList.get(position);
? ?? ???}
? ?? ???@Override
? ?? ???public int getCount() {
? ?? ?? ?? ?return this.mList == null ? 0 : this.mList.size();
? ?? ???}
? ? }
}
? ? 这里唯一注意点的就是两个监听事件,要实现底部导航按钮和页面的联动。


分享到:? QQ好友和群 QQ空间 微信
收藏
收藏0
支持
支持0
反对
反对0
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2? 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )