android介面設計筆記(二)實現頂部底部二級導航欄
下載demo:https://github.com/linliangliang/BottomNavagationBar
二級導航欄的實現是在之前學習導航欄的兩種實現方式的基礎上實現的。
1、https://blog.csdn.net/qq_25066049/article/details/84647988
2、https://blog.csdn.net/qq_25066049/article/details/84646650
首先還是先用BottomNavigation實現底部導航欄,再次基礎上用tabLayout實現二級導航欄,
1、新增依賴:
implementation 'com.android.support:appcompat-v7:26.0.0' compile 'com.ashokvarma.android:bottom-navigation-bar:2.0.3' implementation 'com.android.support:support-v4:26.0.0' compile 'com.android.support:design:26.0.0'
2、主介面佈局activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context="com.zhengyuan.twolevelnavigationbar.MainActivity"> <FrameLayout android:id="@+id/layFrame" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <com.ashokvarma.bottomnavigation.BottomNavigationBar android:id="@+id/bottom_navigation_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" /> </LinearLayout>
3、為一級導航欄寫四個fragment:
fragment_attention.xml
fragment_discovery.xml
fragment_profile.xml
fragment_home.xml
這四個fragment前三個佈局一模一樣,只是fragment_home.xml 因為要新增子導航欄,會有一些不一樣:fragment_home.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:gravity="center" android:orientation="vertical"> <android.support.design.widget.TabLayout android:id="@+id/sub_bottom_tab_layout" android:layout_width="match_parent" android:layout_height="20dp" app:tabIndicatorHeight="1dp" app:tabSelectedTextColor="#03A9F4" app:tabTextColor="@android:color/darker_gray"></android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/subViewPager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollbars="none"></android.support.v4.view.ViewPager> </LinearLayout>
其餘三個只是顯示一個text:
<?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:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="attention" />
</LinearLayout>
4、為一級導航欄寫好佈局後,就可以寫mainActivity.以及為每一個佈局寫對應的實體類了
mainActivity.class:
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
import com.ashokvarma.bottomnavigation.TextBadgeItem;
import com.zhengyuan.twolevelnavigationbar.fragment.FragmentAttention;
import com.zhengyuan.twolevelnavigationbar.fragment.FragmentDiscovery;
import com.zhengyuan.twolevelnavigationbar.fragment.FragmentHome;
import com.zhengyuan.twolevelnavigationbar.fragment.FragmentProfile;
public class MainActivity extends AppCompatActivity implements BottomNavigationBar.OnTabSelectedListener {
private final String TAG = "MainActivity";
private FragmentManager fragmentManager;//管理更新fragment;
private FragmentTransaction fragmentTransaction;
private BottomNavigationBar bottomNavigationBar;
private FragmentProfile fragmentProfile;
private FragmentAttention fragmentAttention;
private FragmentHome fragmentHome;
private FragmentDiscovery fragmentDiscovery;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFragment();
init();
}
private void init() {
bottomNavigationBar = findViewById(R.id.bottom_navigation_bar);
bottomNavigationBar.setMode(BottomNavigationBar.MODE_FIXED);
bottomNavigationBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC);
bottomNavigationBar.setInActiveColor(R.color.colorPrimaryDark);//文字圖片的背景色
bottomNavigationBar.setActiveColor(R.color.colorAccent);//bar背景顏色
TextBadgeItem numberBadgeItem = new TextBadgeItem();
numberBadgeItem.setText("9") //顯示的文字
.setBackgroundColor("#FF0000") //背景色
.setTextColor("#FFFFFF") //文字顏色
.setBorderColor("#000000") //border顏色
.setBorderWidth(5) //border寬度px
.setBackgroundColorResource(R.color.colorPrimaryDark) //背景色,資原始檔獲取
.setBorderColorResource(R.color.colorPrimary) //border顏色,資原始檔獲取
.setTextColorResource(R.color.colorAccent) //文字顏色,資原始檔獲取
.setAnimationDuration(30) //隱藏和展示的動畫速度,單位毫秒,和setHideOnSelect一起使用
.setGravity(Gravity.RIGHT | Gravity.TOP) //位置,預設右上角
.setHideOnSelect(true); //true:當選中狀態時消失,非選中狀態顯示,moren false
//新增並設定圖示、圖示的顏色和文字
bottomNavigationBar
.addItem(new BottomNavigationItem(R.drawable.tab_home_selector, "首頁").setBadgeItem(numberBadgeItem))
.addItem(new BottomNavigationItem(R.drawable.tab_attention_selector, "愛好"))
.addItem(new BottomNavigationItem(R.drawable.tab_discovery_selector, "發現"))
.addItem(new BottomNavigationItem(R.drawable.tab_profile_selector, "我的"))
.setFirstSelectedPosition(0)
.initialise();
bottomNavigationBar.setTabSelectedListener(this);
setDefaultFragment();
}
//設定初始介面
private void setDefaultFragment() {
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.layFrame, FragmentHome.newInstance("首頁"));
fragmentTransaction.commit();
}
private void initFragment() {
Log.i("test==", "首頁== null");
fragmentProfile = FragmentProfile.newInstance("我的");
fragmentAttention = FragmentAttention.newInstance("愛好");
fragmentHome = FragmentHome.newInstance("主頁");
fragmentDiscovery = FragmentDiscovery.newInstance("發現");
}
@Override
public void onTabSelected(int position) {
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
switch (position) {
case 0:
if (fragmentHome == null) {
Log.i("test==", "首頁== null");
fragmentHome = FragmentHome.newInstance("首頁");
}
fragmentTransaction.replace(R.id.layFrame, fragmentHome);
break;
case 1:
if (fragmentAttention == null) {
fragmentAttention = FragmentAttention.newInstance("愛好");
}
fragmentTransaction.replace(R.id.layFrame, fragmentAttention);
break;
case 2:
if (fragmentDiscovery == null) {
fragmentDiscovery = FragmentDiscovery.newInstance("發現");
}
fragmentTransaction.replace(R.id.layFrame, fragmentDiscovery);
break;
case 3:
if (fragmentProfile == null) {
fragmentProfile = FragmentProfile.newInstance("我的");
}
fragmentTransaction.replace(R.id.layFrame, fragmentProfile);
break;
default:
break;
}
//事務提交
fragmentTransaction.commit();
}
@Override
public void onTabUnselected(int position) {
Log.d(TAG, "onTabUnselected() called with: " + "position = [" + position + "]");
}
@Override
public void onTabReselected(int position) {
}
}
為四個fragment寫對應的類:
先寫一個baseFragment:
import android.support.v4.app.Fragment;
import android.view.View;
/**
* Created by 林亮 on 2018/11/29
*/
public abstract class BaseFragment extends Fragment {
/**
* Fragment當前狀態是否可見
*/
public boolean isVisible;
/**
* inflate佈局檔案 返回的view
*/
public View mView;
/**
* 簡化 findViewById
*
* @param viewId
* @param <T>
* @return
*/
protected <T extends View> T find(int viewId) {
return (T) mView.findViewById(viewId);
}
/**
* setUserVisibleHint是在onCreateView之前呼叫的
* 設定Fragment可見狀態
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
/**
* 判斷是否可見
*/
if (getUserVisibleHint()) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInvisible();
}
}
private void onVisible() {
lazyLoad();
}
private void onInvisible() {
}
/**
* 延遲載入
* 子類必須重寫此方法
*/
public abstract void lazyLoad();
}
然後是FragmentAttention.java
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.zhengyuan.bottomnavigationbar.fragment.BaseFragment;
import com.zhengyuan.twolevelnavigationbar.R;
/**
* Created by 林亮 on 2018/11/30
*/
public class FragmentAttention extends BaseFragment {
/**
* 標誌位,標誌已經初始化完成
*/
private boolean isPrepared;
/**
* 是否已被載入過一次,第二次就不再去請求資料了
*/
private boolean mHasLoadedOnce;
TextView textView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mView == null) {
// 需要inflate一個佈局檔案 填充Fragment
mView = inflater.inflate(R.layout.fragment_attention, container, false);
initView();
isPrepared = true;
//實現懶載入
lazyLoad();
}
//快取的mView需要判斷是否已經被加過parent, 如果有parent需要從parent刪除,要不然會發生這個mView已經有parent的錯誤。
ViewGroup parent = (ViewGroup) mView.getParent();
if (parent != null) {
parent.removeView(mView);
}
return mView;
}
/**
* 初始化控制元件
*/
private void initView() {
}
@Override
public void lazyLoad() {
if (!isPrepared || !isVisible || mHasLoadedOnce) {
return;
}
//填充各控制元件的資料
mHasLoadedOnce = true;
}
public static FragmentAttention newInstance(String param1) {
FragmentAttention fragment = new FragmentAttention();
Bundle args = new Bundle();
args.putString("agrs1", param1);
fragment.setArguments(args);
return fragment;
}
}
FragmentDiscovery.java 和FragmentProfile.java兩個類和上面一樣,只是替換佈局,更改對應的類名即可,
如何FargmentHome.calss和上面寫的一樣的話就是一級導航欄,但是我們要在home中實現二級導航欄,就會不一樣:
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.zhengyuan.bottomnavigationbar.fragment.BaseFragment;
import com.zhengyuan.twolevelnavigationbar.R;
import com.zhengyuan.twolevelnavigationbar.adapter.SubFragmentPagerAdapter;
/**
* Created by 林亮 on 2018/11/30
*/
public class FragmentHome extends BaseFragment {
private TabLayout mTabLayout;
private ViewPager mViewPager;
private SubFragmentPagerAdapter subFragmentPagerAdapter;
/**
* 標誌位,標誌已經初始化完成
*/
private boolean isPrepared;
/**
* 是否已被載入過一次,第二次就不再去請求資料了
*/
private boolean mHasLoadedOnce;
TextView textView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (null != mView) {
ViewGroup parent = (ViewGroup) mView.getParent();
if (null != parent) {
parent.removeView(mView);
}
} else {
mView = inflater.inflate(R.layout.fragment_home, null);
initView();// 控制元件初始化
}
mViewPager = find(R.id.subViewPager);
subFragmentPagerAdapter = new SubFragmentPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(subFragmentPagerAdapter);
//將TabLayout與ViewPager繫結在一起
mTabLayout = (TabLayout) find(R.id.sub_bottom_tab_layout);
mTabLayout.setupWithViewPager(mViewPager);
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return mView;
/*//if (mView == null) {
// 需要inflate一個佈局檔案 填充Fragment
mView = inflater.inflate(R.layout.fragment_home, container, false);
initView();
isPrepared = true;
//實現懶載入
//lazyLoad();
//}*/
/*//快取的mView需要判斷是否已經被加過parent, 如果有parent需要從parent刪除,要不然會發生這個mView已經有parent的錯誤。
ViewGroup parent = (ViewGroup) mView.getParent();
if (parent != null) {
parent.removeView(mView);
}
return mView;*/
}
/**
* 初始化控制元件
*/
private void initView() {
Log.i("test==", "FragmentHome initView");
//使用介面卡將ViewPager與Fragment繫結在一起
}
@Override
public void lazyLoad() {
if (!isPrepared || !isVisible || mHasLoadedOnce) {
return;
}
//填充各控制元件的資料
mHasLoadedOnce = true;
}
public static FragmentHome newInstance(String param1) {
FragmentHome fragment = new FragmentHome();
Bundle args = new Bundle();
args.putString("agrs1", param1);
fragment.setArguments(args);
return fragment;
}
}
5、當然還需要為二級導航欄寫三個fragment和三個對應的class
如下:sub_fragment_first.xml
<?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:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="subFragmentFirst" />
</LinearLayout>
sub_fragment_second.xml
sub_fragment_third.xml一樣只顯示一個text,不再重複貼程式碼
有了子fragment還需要為他們新增對應的類:FragmentSubFirst
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.zhengyuan.twolevelnavigationbar.R;
/**
* Created by 林亮 on 2018/11/30
*/
public class FragmentSubFirst extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.sub_fragment_first, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
其餘兩個如法炮製。
6、然後為二級導航欄新增一個介面卡SubFragmentPagerAdapter.java:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import com.zhengyuan.twolevelnavigationbar.subfragment.FragmentSubFirst;
import com.zhengyuan.twolevelnavigationbar.subfragment.FragmentSubSecond;
import com.zhengyuan.twolevelnavigationbar.subfragment.FragmentSubThird;
/**
* Created by 林亮 on 2018/11/30
*/
public class SubFragmentPagerAdapter extends FragmentPagerAdapter {
private String[] mTitles = new String[]{"愛好", "發現", "主頁"};
public SubFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return new FragmentSubFirst();
} else if (position == 1) {
return new FragmentSubSecond();
}
return new FragmentSubThird();
}
@Override
public int getCount() {
return mTitles.length;
}
//ViewPager與TabLayout繫結後,這裡獲取到PageTitle就是Tab的Text
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
}
7、程式碼就完成了其中涉及的文圖,需要匯入資源。