前言
這一篇咱們完成沙發頁和啓動頁,以及沉浸式狀態欄java
實現沙發頁
看下UI,主要就是兩部分組成,上面是個TabLayout
,下面是個ViewPager
咱們在作首頁底部Tab
的時候用的是json
配置的,咱們這個上面的TabLayout
也用一個json
來配置
android
{ "activeSize": 16, "normalSize": 14, "activeColor": "#ED7282", "normalColor": "#666666", "select": 0, "tabGravity": 0, "tabs": [ { "title": "圖片", "index": 0, "tag": "pics", "enable": true }, { "title": "視頻", "index": 1, "tag": "video", "enable": true }, { "title": "文本", "index": 1, "tag": "text", "enable": true } ] }
簡單解釋下,分別是選中字體大小,正常字體大小,選中字體顏色,正常字體顏色,選中第幾個,位置,標籤,而後咱們定義一個對應的JavaBean
json
public class SofaTab { /** * activeSize : 16 * normalSize : 14 * activeColor : #ED7282 * normalColor : #666666 * select : 0 * tabGravity : 0 * tabs : [{"title":"圖片","index":0,"tag":"pics","enable":true},{"title":"視頻","index":1,"tag":"video","enable":true},{"title":"文本","index":1,"tag":"text","enable":true}] */ public int activeSize; public int normalSize; public String activeColor; public String normalColor; public int select; public int tabGravity; public List<Tabs> tabs; public static class Tabs { /** * title : 圖片 * index : 0 * tag : pics * enable : true */ public String title; public int index; public String tag; public boolean enable; } }
咱們以前解析BottomBar
是在AppConfig
中作的,咱們這個也在這裏面獲取沙發相關的Tab
app
public static SofaTab getSofaTabConfig() { if (sSofaTab == null) { String content = parseFile("sofa_tabs_config.json"); sSofaTab = JSON.parseObject(content, SofaTab.class); Collections.sort(sSofaTab.tabs, new Comparator<SofaTab.Tabs>() { @Override public int compare(SofaTab.Tabs o1, SofaTab.Tabs o2) { return o1.index < o2.index ? -1 : 1; } }); } return sSofaTab; }
接着咱們就改造下咱們的fragment_sofa
這個佈局文件,裏面應該是有兩個控件的ide
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="@dimen/dp_45" app:tabGravity="center" app:tabIndicatorColor="@color/color_theme" app:tabIndicatorFullWidth="false" app:tabIndicatorHeight="@dimen/dp_2" app:tabInlineLabel="true" app:tabMode="scrollable" app:tabSelectedTextColor="@color/color_theme" app:tabTextColor="@color/color_333" app:tabUnboundedRipple="true" /> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" /> </LinearLayout> </layout>
TabLayout
的用法這裏就不在多說了,這裏就稍微說下這個ViewPager2
,這個能夠算是 ViewPager
的升級版,內部使用的是RecycleView
實現相似ViewPager
的效果,同時也解決了ViewPager
預加載的問題,使用起來也很簡單佈局
//限制頁面預加載 viewPager2.setOffscreenPageLimit(ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT); //viewPager2默認只有一種類型的Adapter。FragmentStateAdapter //而且在頁面切換的時候 不會調用子Fragment的setUserVisibleHint ,取而代之的是onPause(),onResume()、 viewPager2.setAdapter(new FragmentStateAdapter(getChildFragmentManager(), this.getLifecycle()) { @NonNull @Override public Fragment createFragment(int position) { //這裏不須要本身保管了,FragmentStateAdapter內部本身會管理已實例化的fragment對象。 return getTabFragment(position); } @Override public int getItemCount() { return tabs.size(); } }); tabLayout.setTabGravity(tabConfig.tabGravity); //viewPager2 就不能和再用TabLayout.setUpWithViewPager()了 //取而代之的是TabLayoutMediator。咱們能夠在onConfigureTab()方法的回調裏面 作tab標籤的配置 //其中autoRefresh的意思是:若是viewPager2 中child的數量發生了變化,也即咱們調用了adapter#notifyItemChanged()先後getItemCount不一樣。 //要不要 從新刷野tabLayout的tab標籤。視狀況而定,像我們sofaFragment的tab數量一旦固定了是不會變的,傳true/false 都問題不大 mediator = new TabLayoutMediator(tabLayout, viewPager2, true, new TabLayoutMediator.TabConfigurationStrategy() { @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) { tab.setCustomView(makeTabView(position)); } }); mediator.attach(); viewPager2.registerOnPageChangeCallback(mPageChangeCallback); //切換到默認選擇項,那固然要等待初始化完成以後纔有效 viewPager2.post(() -> viewPager2.setCurrentItem(tabConfig.select, false));
ViewPager2
裏面默認的適配器只有一種FragmentStateAdapter
,相對於ViewPager
來講配合TabLayout
的使用方法有些不一樣,這裏須要一個TabLayoutMediator
,對應的就是attach()
和detach()
方法,監聽頁面變化改成registerOnPageChangeCallback
,具體的詳細使用能夠參考下其餘的文章,這裏就不在贅述了post
public class SofaFragment extends Fragment { private FragmentSofaBinding binding; protected ViewPager2 viewPager2; protected TabLayout tabLayout; private SofaTab tabConfig; private ArrayList<SofaTab.Tabs> tabs; private TabLayoutMediator mediator; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentSofaBinding.inflate(inflater, container, false); return binding.getRoot(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { viewPager2 = binding.viewPager; tabLayout = binding.tabLayout; tabConfig = getTabConfig(); tabs = new ArrayList<>(); for (SofaTab.Tabs tab : tabConfig.tabs) { if (tab.enable) { tabs.add(tab); } } //限制頁面預加載 viewPager2.setOffscreenPageLimit(ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT); //viewPager2默認只有一種類型的Adapter。FragmentStateAdapter //而且在頁面切換的時候 不會調用子Fragment的setUserVisibleHint ,取而代之的是onPause(),onResume()、 viewPager2.setAdapter(new FragmentStateAdapter(getChildFragmentManager(), this.getLifecycle()) { @NonNull @Override public Fragment createFragment(int position) { //這裏不須要本身保管了,FragmentStateAdapter內部本身會管理已實例化的fragment對象。 return getTabFragment(position); } @Override public int getItemCount() { return tabs.size(); } }); tabLayout.setTabGravity(tabConfig.tabGravity); //viewPager2 就不能和再用TabLayout.setUpWithViewPager()了 //取而代之的是TabLayoutMediator。咱們能夠在onConfigureTab()方法的回調裏面 作tab標籤的配置 //其中autoRefresh的意思是:若是viewPager2 中child的數量發生了變化,也即咱們調用了adapter#notifyItemChanged()先後getItemCount不一樣。 //要不要 從新刷野tabLayout的tab標籤。視狀況而定,像我們sofaFragment的tab數量一旦固定了是不會變的,傳true/false 都問題不大 mediator = new TabLayoutMediator(tabLayout, viewPager2, true, new TabLayoutMediator.TabConfigurationStrategy() { @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) { tab.setCustomView(makeTabView(position)); } }); mediator.attach(); viewPager2.registerOnPageChangeCallback(mPageChangeCallback); //切換到默認選擇項,那固然要等待初始化完成以後纔有效 viewPager2.post(() -> viewPager2.setCurrentItem(tabConfig.select, false)); } ViewPager2.OnPageChangeCallback mPageChangeCallback = new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { int tabCount = tabLayout.getTabCount(); for (int i = 0; i < tabCount; i++) { TabLayout.Tab tab = tabLayout.getTabAt(i); TextView customView = (TextView) tab.getCustomView(); if (tab.getPosition() == position) { customView.setTextSize(tabConfig.activeSize); customView.setTypeface(Typeface.DEFAULT_BOLD); } else { customView.setTextSize(tabConfig.normalSize); customView.setTypeface(Typeface.DEFAULT); } } } }; private View makeTabView(int position) { TextView tabView = new TextView(getContext()); int[][] states = new int[2][]; states[0] = new int[]{android.R.attr.state_selected}; states[1] = new int[]{}; int[] colors = new int[]{Color.parseColor(tabConfig.activeColor), Color.parseColor(tabConfig.normalColor)}; ColorStateList stateList = new ColorStateList(states, colors); tabView.setTextColor(stateList); tabView.setText(tabs.get(position).title); tabView.setTextSize(tabConfig.normalSize); return tabView; } public Fragment getTabFragment(int position) { return HomeFragment.newInstance(tabs.get(position).tag); } public SofaTab getTabConfig() { return AppConfig.getSofaTabConfig(); } @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); List<Fragment> fragments = getChildFragmentManager().getFragments(); for (Fragment fragment : fragments) { if (fragment.isAdded() && fragment.isVisible()) { fragment.onHiddenChanged(hidden); break; } } } @Override public void onDestroy() { mediator.detach(); viewPager2.unregisterOnPageChangeCallback(mPageChangeCallback); super.onDestroy(); } }
實現啓動頁,消除白屏
咱們若是不配置啓動頁的話,咱們的App
啓動的時候可能會有一會白屏,而後纔會顯示咱們的內容,啓動分冷啓動
和熱啓動
,這種出現白屏通常都是冷啓動
發生的,解決辦法網上有不少,咱們這裏採用一種簡單高效的方式
咱們首先給咱們的入口類Activity
配置一個特殊的主題,不使用默認的主題
字體
<style name="launcher" parent="AppTheme"> <item name="android:windowFullscreen">true</item> <item name="android:windowBackground">@drawable/splash</item> </style>
而後給咱們的MainActivity
設置主題ui
<activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
接着咱們在MainActivity
的onCreate
方法中設置回正常的主題便可this
@Override protected void onCreate(Bundle savedInstanceState) { //因爲 啓動時設置了 R.style.launcher 的windowBackground屬性 //勢必要在進入主頁後,把窗口背景清理掉 setTheme(R.style.AppTheme); super.onCreate(savedInstanceState); ... }
實現沉浸式狀態欄
public class StatusBar { /** * 6.0級以上的沉浸式佈局 * * @param activity */ public static void fitSystemBar(Activity activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; Window window = activity.getWindow(); View decorView = window.getDecorView(); //View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN--可以使得咱們的頁面佈局延伸到狀態欄之下,但不會隱藏狀態欄。也就至關於狀態欄是遮蓋在佈局之上的 //View.SYSTEM_UI_FLAG_FULLSCREEN -- 可以使得咱們的頁面佈局延伸到狀態欄,可是會隱藏狀態欄。 //WindowManager.LayoutParams.FLAG_FULLSCREEN decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.setStatusBarColor(Color.TRANSPARENT); } /** * 6.0及以上的狀態欄色調 * * @param activity * @param light true:白底黑字,false:黑底白字 */ public static void lightStatusBar(Activity activity, boolean light) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; Window window = activity.getWindow(); View decorView = window.getDecorView(); int visibility = decorView.getSystemUiVisibility(); if (light) { visibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } else { visibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } decorView.setSystemUiVisibility(visibility); } }
OK,最後咱們看下效果