Android 动态式换肤框架3-Fragment、状态栏换肤

可以先看前两篇文章:

Android 动态式换肤框架1-setContentView源码分析:
https://blog.csdn.net/hongxue8888/article/details/95494195

Android 动态式换肤框架2-实现背景替换:
https://blog.csdn.net/hongxue8888/article/details/95390639


上篇文章实现了Activity的背景换肤,接下来实现以下的换肤:

  • Fragment换肤
  • 状态栏换肤

1 Fragment 换肤

1.1 Fragment 换肤实现

继续使用上篇文章中的代码,在MainActivity中添加三个Fragment。

activity_main.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:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="skinSelect"
        android:text="个性换肤" />

    <!--测试换肤==> viewPager 与 fragment-->
    <com.hongx.hxskin.widget.MyTabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:tabIndicatorColor="@color/tabSelectedTextColor"
        app:tabTextColor="@color/tab_selector"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

MainActivity的onCreate方法中

  		MyTabLayout tabLayout = findViewById(R.id.tabLayout);
        ViewPager viewPager = findViewById(R.id.viewPager);
        List<Fragment> list = new ArrayList<>();
        list.add(new MusicFragment());
        list.add(new VideoFragment());
        list.add(new RadioFragment());
        List<String> listTitle = new ArrayList<>();
        listTitle.add("音乐");
        listTitle.add("视频");
        listTitle.add("电台");
        MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter
                (getSupportFragmentManager(), list, listTitle);
        viewPager.setAdapter(myFragmentPagerAdapter);
        tabLayout.setupWithViewPager(viewPager);

在MusicFragment中添加一个和MainActivity一样的背景:

<?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"
    android:background="@drawable/t_window_bg"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="音乐"
        android:textColor="@color/colorAccent"
        android:textSize="22sp" />
</LinearLayout>

查看效果:
在这里插入图片描述

什么也没做,同样实现了Fragment的换肤,为什么?

1.2 源码分析

打开 /android.support.v4.app.Fragment.java
搜索LayoutInflater会发现onGetLayoutInflater方法中创建了LayoutInflater

  public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
        return this.getLayoutInflater(savedInstanceState);
    }
    FragmentHostCallback mHost;
	...
	
    public LayoutInflater getLayoutInflater(@Nullable Bundle savedFragmentState) {
        if (this.mHost == null) {
            throw new IllegalStateException("onGetLayoutInflater() cannot be executed until the Fragment is attached to the FragmentManager.");
        } else {
            LayoutInflater result = this.mHost.onGetLayoutInflater();//1
            this.getChildFragmentManager();
            LayoutInflaterCompat.setFactory2(result, this.mChildFragmentManager.getLayoutInflaterFactory());//2
            return result;
        }
    }

注释1:通过mHost.onGetLayoutInflater()创建了LayoutInflater对象result
注释2:设置Factory2


/android.support.v4.app.FragmentHostCallback.java

    public LayoutInflater onGetLayoutInflater() {
        return LayoutInflater.from(this.mContext);
    }

/android.support.v4.view.LayoutInflaterCompat.java

  public static void setFactory2(@NonNull LayoutInflater inflater, @NonNull Factory2 factory) {
        inflater.setFactory2(factory);
        if (VERSION.SDK_INT < 21) {
            Factory f = inflater.getFactory();
            if (f instanceof Factory2) {
                forceSetFactory2(inflater, (Factory2)f);
            } else {
                forceSetFactory2(inflater, factory);
            }
        }

    }

/android.view.LayoutInflater.java

    public void setFactory2(Factory2 factory) {
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
    }

Fragment 会走

  mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
    private static class FactoryMerger implements Factory2 {
        private final Factory mF1, mF2;
        private final Factory2 mF12, mF22;

        FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
            mF1 = f1;
            mF2 = f2;
            mF12 = f12;
            mF22 = f22;
        }

        public View onCreateView(String name, Context context, AttributeSet attrs) {
            View v = mF1.onCreateView(name, context, attrs);
            if (v != null) return v;
            return mF2.onCreateView(name, context, attrs);
        }

        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
                    : mF1.onCreateView(name, context, attrs);
            if (v != null) return v;
            return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
                    : mF2.onCreateView(name, context, attrs);
        }
    }

FactoryMerger实现了工厂合并。


2 状态栏换肤

首先需要将app和app_skin中分别设置colorPrimaryDark为两种不同的颜色。

<color name="colorPrimaryDark">#00574B</color>
 <color name="colorPrimaryDark">#ffce3d3a</color>

在SkinThemeUtils.java中添加如下代码:

    private static int[] APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS = {
            android.support.v7.appcompat.R.attr.colorPrimaryDark
    };
    private static int[] STATUSBAR_COLOR_ATTRS = {android.R.attr.statusBarColor, android.R.attr
            .navigationBarColor};

    public static void updateStatusBarColor(Activity activity) {
        //5.0以上才能修改
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return;
        }
        //获得 statusBarColor 与 nanavigationBarColor (状态栏颜色)
        //当与 colorPrimaryDark  不同时 以statusBarColor为准
        int[] statusBarColorResId = getResId(activity, STATUSBAR_COLOR_ATTRS);
        //如果直接在style中写入固定颜色值(而不是 @color/XXX ) 获得0
        if (statusBarColorResId[0] != 0) {
            activity.getWindow().setStatusBarColor(SkinResources.getInstance().getColor
                    (statusBarColorResId[0]));
        } else {
            //获得 colorPrimaryDark
            int colorPrimaryDarkResId = getResId(activity, APPCOMPAT_COLOR_PRIMARY_DARK_ATTRS)[0];
            if (colorPrimaryDarkResId != 0) {
                activity.getWindow().setStatusBarColor(SkinResources.getInstance().getColor
                        (colorPrimaryDarkResId));
            }
        }
        if (statusBarColorResId[1] != 0) {
            activity.getWindow().setNavigationBarColor(SkinResources.getInstance().getColor
                    (statusBarColorResId[1]));
        }
    }

在SkinLayoutInflaterFactory的update方法中去执行更换颜色

 @Override
    public void update(Observable o, Object arg) {
        SkinThemeUtils.updateStatusBarColor(activity);
    }

为了使下次进入后显示换肤后的颜色,需要在SkinActivityLifecycle的onActivityCreated方法中添加换肤操作。

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        /**
         *  更新状态栏
         */
        SkinThemeUtils.updateStatusBarColor(activity);
    }

看状态栏换肤效果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值