【Android】Jetpack全組件實戰開發短視頻應用App(十六)

前言

這一篇咱們完成沙發頁和啓動頁,以及沉浸式狀態欄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
    }
  ]
}

簡單解釋下,分別是選中字體大小,正常字體大小,選中字體顏色,正常字體顏色,選中第幾個,位置,標籤,而後咱們定義一個對應的JavaBeanjson

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中作的,咱們這個也在這裏面獲取沙發相關的Tabapp

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>

接着咱們在MainActivityonCreate方法中設置回正常的主題便可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,最後咱們看下效果
在這裏插入圖片描述

相關文章
相關標籤/搜索