博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
带你过一遍Android 多主题框架——MagicaSakura
阅读量:4294 次
发布时间:2019-05-27

本文共 32085 字,大约阅读时间需要 106 分钟。

MagicaSakura 是 Android 多主题框架。

具有以下优点:

  • 列表内容
  • 列表内容
  • 支持白天彩色主题和夜间主题。
  • 切换主题不需要重建activity
  • 提供TintXXX控件适配不同的主题,更方便更快捷。
  • 只需写一个drawable.xml 或者 layout.xml 就能自动适配到不同主题样式。
  • 兼容4.0.3以上
  • 易于集成到你的app。

首先进入Main的布局

对应Main怎么进入入口

//设置titleToolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);getSupportActionBar().setTitle(null);//设置中间竖直可滑动部分RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);LinearLayoutManager layoutManager = new LinearLayoutManager(this);layoutManager.setSmoothScrollbarEnabled(true);//进行分割线的操作recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        //8dp        final int padding = getResources().getDimensionPixelOffset(R.dimen.padding_half);        //返回适配器条目的位置        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();        final int position = layoutParams.getViewLayoutPosition();        if (position == 0) {            outRect.left = outRect.top = outRect.right = padding;            outRect.bottom = padding >> 1;        } else if (position == state.getItemCount() - 1) {            outRect.left = outRect.bottom = outRect.right = padding;            outRect.top = padding >> 1;        } else {            outRect.left = outRect.right = padding;            outRect.top = outRect.bottom = padding >> 1;        }    }});//设置进行水平还是垂直recyclerView.setLayoutManager(layoutManager);//设置适配器Adapter adapter = new Adapter();recyclerView.setAdapter(adapter);//适配器添加HolderTypeadapter.addViewHolderType(        ViewHolder.VIEW_HOLDER_HEADER,        ViewHolder.VIEW_HOLDER_LABEL,        ViewHolder.VIEW_HOLDER_HEADER,        ViewHolder.VIEW_HOLDER_LOGIN,        ViewHolder.VIEW_HOLDER_HEADER,        ViewHolder.VIEW_HOLDER_DOWNLOAD);
这里明白一个知识点
  1. 关于Rect outRect这个参数
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {            @Override            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {            }}

在这个参数中Rect outRect这个矩形控制条目的显示

- outRect.left : 控制条目距离左边的距离
- outRect.right:条目距离右边的距离
- outRect.top : 条目距离上边的距离
- outRect.bottom : 条目距离下边的距离

  1. 关于padding >> 1

相当于 padding/2的值

接下来我们就看看适配器
public static class Adapter extends RecyclerView.Adapter
{
List
viewHolderTypes = new ArrayList<>();//视图类型 SparseArrayCompat
titleIndexs = new SparseArrayCompat<>();//标题集合 @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return ViewHolder.create(parent, viewType);根据类型创建每一个条目 } @Override public void onBindViewHolder(ViewHolder holder, int position) { //将数据绑定到item视图上 if (holder instanceof ViewHolderHeader) { ((ViewHolderHeader) holder).setTitle(titleIndexs.get(position)); } } //条目集合数 @Override public int getItemCount() { return viewHolderTypes.size(); } //根据位置得到type信息 @Override public int getItemViewType(int position) { return viewHolderTypes.get(position); } // public void addViewHolderType(int... type) { for (int i = 0; i < type.length; i++) { if (type[i] == ViewHolder.VIEW_HOLDER_HEADER) { titleIndexs.put(i, titleIndexs.size() + 1); } viewHolderTypes.add(type[i]); } notifyDataSetChanged(); }}

对于适配器,最次也要实现以下几个方法

//onCreateViewHolder()负责为Item创建视图@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {    return null;}//onBindViewHolder()负责将数据绑定到Item的视图上@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {}//条目个数@Overridepublic int getItemCount() {    return 0;}

根据type创建对应的ViewHolder

public static abstract class ViewHolder extends RecyclerView.ViewHolder {
public static final int VIEW_HOLDER_HEADER = 0; public static final int VIEW_HOLDER_LABEL = VIEW_HOLDER_HEADER + 1; public static final int VIEW_HOLDER_LOGIN = VIEW_HOLDER_LABEL + 1; public static final int VIEW_HOLDER_DOWNLOAD = VIEW_HOLDER_LOGIN + 1; public ViewHolder(View itemView) { super(itemView); } public static ViewHolder create(ViewGroup viewHolder, int type) { switch (type) { case VIEW_HOLDER_HEADER: return ViewHolderHeader.create(viewHolder);//[1.1] case VIEW_HOLDER_LABEL: return ViewHolderLabel.create(viewHolder); case VIEW_HOLDER_LOGIN: return ViewHolderLogin.create(viewHolder); case VIEW_HOLDER_DOWNLOAD: return ViewHolderChoice.create(viewHolder); default: return null; } }}

1.1 通过上面的调用开始初始化布局

public static class ViewHolderHeader extends ViewHolder {
private static final String[] sTitles = new String[]{
"Label", "Login", "Choice"}; TintImageView icon; TextView title; public ViewHolderHeader(View itemView) { super(itemView); icon = (TintImageView) itemView.findViewById(R.id.icon); title = (TextView) itemView.findViewById(R.id.title); } public void setTitle(int index) { title.setText(sTitles[index - 1]); icon.setImageResource(itemView.getResources().getIdentifier( "ic_looks_" + index, "drawable", itemView.getContext().getPackageName())); icon.setImageTintList(R.color.theme_color_primary); } //对每一种单独type的条目进行初始化 public static ViewHolderHeader create(ViewGroup parent) { return new ViewHolderHeader(LayoutInflater.from( parent.getContext()).inflate(R.layout.layout_list_item_header, parent, false)); }}

补充知识点

icon.setImageResource(itemView.getResources().getIdentifier("ic_looks_" + index, "drawable", itemView.getContext().getPackageName()));

其中getIdentifier得到的是resId,这个图片的名称是:ic_looks_xxx,其中第二个参数得到的是drawable,raw,id这些类型名称,第三个参数是包名

其中ViewHolderHeader这些是根据type进行不同的配置的原理相同,现在我们就看看里面有什么补充点

1

title.setCompoundDrawablesWithIntrinsicBounds(!isChecked ? R.drawable.selector_lock : R.drawable.selector_unlock, 0, 0, 0);

设置一个图标在此TextView的上下左右四个不同的位置

主题

public class MyApplication extends Application implements ThemeUtils.switchColor {
@Override public void onCreate() { super.onCreate(); //使用MyApplication中实现的接口 ThemeUtils.setSwitchColor(this); } //对应switchColor接口中的replaceColorById @Override public int replaceColorById(Context context, @ColorRes int colorId) { //判断是不是默认主题色 if (ThemeHelper.isDefaultTheme(context)) { return context.getResources().getColor(colorId); } //得到当前主题色 String theme = getTheme(context); if (theme != null) { colorId = getThemeColorId(context, colorId, theme); } return context.getResources().getColor(colorId); } //对应switchColor接口中的replaceColor,进行更换颜色 @Override public int replaceColor(Context context, @ColorInt int originColor) { //默认主题 if (ThemeHelper.isDefaultTheme(context)) { return originColor; } String theme = getTheme(context); int colorId = -1; if (theme != null) { colorId = getThemeColor(context, originColor, theme); } return colorId != -1 ? getResources().getColor(colorId) : originColor; } private String getTheme(Context context) { if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_STORM) { return "blue"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_HOPE) { return "purple"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_WOOD) { return "green"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_LIGHT) { return "green_light"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_THUNDER) { return "yellow"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_SAND) { return "orange"; } else if (ThemeHelper.getTheme(context) == ThemeHelper.CARD_FIREY) { return "red"; } return null; } //得到colorid private @ColorRes int getThemeColorId(Context context, int colorId, String theme) { switch (colorId) { case R.color.theme_color_primary: return context.getResources().getIdentifier(theme, "color", getPackageName()); case R.color.theme_color_primary_dark: return context.getResources().getIdentifier(theme + "_dark", "color", getPackageName()); case R.color.theme_color_primary_trans: return context.getResources().getIdentifier(theme + "_trans", "color", getPackageName()); } return colorId; } private @ColorRes int getThemeColor(Context context, int color, String theme) { switch (color) { case 0xfffb7299: return context.getResources().getIdentifier(theme, "color", getPackageName()); case 0xffb85671: return context.getResources().getIdentifier(theme + "_dark", "color", getPackageName()); case 0x99f0486c: return context.getResources().getIdentifier(theme + "_trans", "color", getPackageName()); } return -1; }}

改变主题入口

//这个方法是TitleBar的方法,弹出一个Dialog@Overridepublic boolean onOptionsItemSelected(MenuItem item) {    if (item.getItemId() == R.id.change_theme) {        CardPickerDialog dialog = new CardPickerDialog();        dialog.setClickListener(this);        dialog.show(getSupportFragmentManager(), CardPickerDialog.TAG);        return true;    }    return super.onOptionsItemSelected(item);}

自定义Dialog CardPickerDialog

 

比如当前设置pink主题

case R.id.theme_pink:        mCurrentTheme = ThemeHelper.CARD_SAKURA;        setImageButtons(mCurrentTheme);        break;

当点击确定时

if (mClickListener != null) {    mClickListener.onConfirm(mCurrentTheme);}
@Overridepublic void onConfirm(int currentTheme) {    if (ThemeHelper.getTheme(MainActivity.this) != currentTheme) {        //当不是当前主题时进行设置,写入sp内        ThemeHelper.setTheme(MainActivity.this, currentTheme);        //        ThemeUtils.refreshUI(MainActivity.this, new ThemeUtils.ExtraRefreshable() {                    @Override                    public void refreshGlobal(Activity activity) {                        //for global setting, just do once                        if (Build.VERSION.SDK_INT >= 21) {                            //设置全局的标题栏                            final MainActivity context = MainActivity.this;                            ActivityManager.TaskDescription taskDescription =                                    new ActivityManager.TaskDescription(null, null,                                            ThemeUtils.getThemeAttrColor(context, android.R.attr.colorPrimary));                            setTaskDescription(taskDescription);                            //设置状态栏颜色                            getWindow().setStatusBarColor(                                    ThemeUtils.getColorById(context, R.color.theme_color_primary_dark));                        }                    }                    @Override                    public void refreshSpecificView(View view) {                    }                }        );        //底部显示一个黑框信息        View view = findViewById(R.id.snack_layout);        if (view != null) {            TextView textView = (TextView) view.findViewById(R.id.content);            textView.setText(getSnackContent(currentTheme));            SnackAnimationUtil.with(this, R.anim.snack_in, R.anim.snack_out)                    .setDismissDelayTime(1000)                    .setTarget(view)                    .play();        }    }}

如下核心代码

//得到系统定义的contentviewView rootView = activity.getWindow().getDecorView().findViewById(android.R.id.content);refreshView(rootView, extraRefreshable);
private static void refreshView(View view, ExtraRefreshable extraRefreshable) {        if (view == null) return;        view.destroyDrawingCache();        //如果View继承了Tintable,则利用tint方法去处理,我们稍后看这个方法如何处理        if (view instanceof Tintable) {            ((Tintable) view).tint();            //当处理后递归遍历该view容器下的其他控件            if (view instanceof ViewGroup) {                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {                    refreshView(((ViewGroup) view).getChildAt(i), extraRefreshable);                }            }        } else {            //            if (extraRefreshable != null) {                extraRefreshable.refreshSpecificView(view);            }            if (view instanceof AbsListView) {                try {                    //final RecycleBin mRecycler = new RecycleBin();对应拿到这个引用                    if (sRecyclerBin == null) {                        sRecyclerBin = AbsListView.class.getDeclaredField("mRecycler");                        sRecyclerBin.setAccessible(true);                    }                    if (sListViewClearMethod == null) {                        //拿到RecycleBin中的clear方法                        sListViewClearMethod = Class.forName("android.widget.AbsListView$RecycleBin")                                .getDeclaredMethod("clear");                        sListViewClearMethod.setAccessible(true);                    }                    //执行clear方法进行所有缓存view的清除                    sListViewClearMethod.invoke(sRecyclerBin.get(view));                } catch (NoSuchFieldException e) {                    e.printStackTrace();                } catch (ClassNotFoundException e) {                    e.printStackTrace();                } catch (NoSuchMethodException e) {                    e.printStackTrace();                } catch (InvocationTargetException e) {                    e.printStackTrace();                } catch (IllegalAccessException e) {                    e.printStackTrace();                }                //更新每一个视图                ListAdapter adapter = ((AbsListView) view).getAdapter();                while (adapter instanceof WrapperListAdapter) {                    adapter = ((WrapperListAdapter) adapter).getWrappedAdapter();                }                if (adapter instanceof BaseAdapter) {                    ((BaseAdapter) adapter).notifyDataSetChanged();                }            }            if (view instanceof RecyclerView) {                try {                    if (sRecycler == null) {                        sRecycler = RecyclerView.class.getDeclaredField("mRecycler");                        sRecycler.setAccessible(true);                    }                    if (sRecycleViewClearMethod == null) {                        sRecycleViewClearMethod = Class.forName("android.support.v7.widget.RecyclerView$Recycler")                                .getDeclaredMethod("clear");                        sRecycleViewClearMethod.setAccessible(true);                    }                    sRecycleViewClearMethod.invoke(sRecycler.get(view));                } catch (NoSuchMethodException e) {                    e.printStackTrace();                } catch (IllegalAccessException e) {                    e.printStackTrace();                } catch (NoSuchFieldException e) {                    e.printStackTrace();                } catch (InvocationTargetException e) {                    e.printStackTrace();                } catch (ClassNotFoundException e) {                    e.printStackTrace();                }                ((RecyclerView) view).getRecycledViewPool().clear();                ((RecyclerView) view).invalidateItemDecorations();            }            if (view instanceof ViewGroup) {                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {                    refreshView(((ViewGroup) view).getChildAt(i), extraRefreshable);                }            }        }    }

这样相当于核心又落到了Tintable.tint()方法

我们就先拿TintView说起

public class TintView extends View implements Tintable, AppCompatBackgroundHelper.BackgroundExtensible {
private AppCompatBackgroundHelper mBackgroundHelper; public TintView(Context context) { this(context, null); } public TintView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TintView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //编辑模式就退出 if (isInEditMode()) { return; } //此时理解成操作类 TintManager tintManager = TintManager.get(context);//[3.1] //利用AppCompatBackgroundHelper组合当前控件和操作类TintManager mBackgroundHelper = new AppCompatBackgroundHelper(this, tintManager);//[3.2] mBackgroundHelper.loadFromAttribute(attrs, defStyleAttr);//[3.3] } ...}

现在我们就开始通过构造入口了解

3.1 TintManager.get(context)

目的很简单就是根据context生成对应的TintManager,然后添加到名称是INSTANCE_CACHE的WeakHashMap弱引用Map中

private static final WeakHashMap
INSTANCE_CACHE = new WeakHashMap<>();public static com.bilibili.magicasakura.utils.TintManager get(Context context) { if (context == null) return null; if (context instanceof ContextThemeWrapper) { context = ((ContextThemeWrapper) context).getBaseContext(); } if (context instanceof android.view.ContextThemeWrapper) { context = ((android.view.ContextThemeWrapper) context).getBaseContext(); } com.bilibili.magicasakura.utils.TintManager tm = INSTANCE_CACHE.get(context); if (tm == null) { tm = new com.bilibili.magicasakura.utils.TintManager(context);//3.1.1 INSTANCE_CACHE.put(context, tm); } return tm;}

3.1.1 TintManager构造

private TintManager(Context context) {    mContextRef = new WeakReference<>(context);}

3.2 new AppCompatBackgroundHelper(this, tintManager)

就是一个存入的功能

public AppCompatBaseHelper(T view, TintManager tintManager) {    mView = view;    mTintManager = tintManager;}public AppCompatBackgroundHelper(View view, TintManager tintManager) {    super(view, tintManager);}

3.3 AppCompatBackgroundHelper.loadFromAttribute()

@SuppressWarnings("ResourceType")@Overridevoid loadFromAttribute(AttributeSet attrs, int defStyleAttr) {    initPadding();//得到view的上下左右padding    //得到早定义的属性    TypedArray array = mView.getContext().obtainStyledAttributes(attrs, R.styleable.TintViewBackgroundHelper, defStyleAttr, 0);    //如果backgroundTint这个参数有值    if (array.hasValue(R.styleable.TintViewBackgroundHelper_backgroundTint)) {        //设置值        mBackgroundTintResId = array.getResourceId(R.styleable.TintViewBackgroundHelper_backgroundTint, 0);        //之后看backgroundTintMode设置的模式是什么        if (array.hasValue(R.styleable.TintViewBackgroundHelper_backgroundTintMode)) {            //先进行解析模式,然后进行设置            //这里进行[3.3.1],[3.3.2]处理            setSupportBackgroundTintMode(DrawableUtils.parseTintMode(array.getInt(R.styleable.TintViewBackgroundHelper_backgroundTintMode, 0), null));        }        setSupportBackgroundTint(mBackgroundTintResId);//[3.3.3]    } else {        //如果没有设置backgroundTint,那就看android_background设置的值,如果设置了就设置背景了        Drawable drawable = mTintManager.getDrawable(mBackgroundResId = array.getResourceId(R.styleable.TintViewBackgroundHelper_android_background, 0));        if (drawable != null) {            setBackgroundDrawable(drawable);        }    }    array.recycle();}

3.3.1 DrawableUtils.parseTintMode(array.getInt(R.styleable.TintViewBackgroundHelper_backgroundTintMode, 0), null)

返回对应的mode

public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {    switch (value) {        case 3:            return PorterDuff.Mode.SRC_OVER;        case 5:            return PorterDuff.Mode.SRC_IN;        case 9:            return PorterDuff.Mode.SRC_ATOP;        case 14:            return PorterDuff.Mode.MULTIPLY;        case 15:            return PorterDuff.Mode.SCREEN;        case 16:            return Build.VERSION.SDK_INT >= 11 ? PorterDuff.Mode.valueOf("ADD")                    : defaultMode;        default:            return defaultMode;    }}

3.3.2

当mode不等于null的时候用一个TintInfo记录,我们需要记住mHasTintMode,mTintMode已经有值此时

private void setSupportBackgroundTintMode(PorterDuff.Mode mode) {    if (mBackgroundTintResId != 0 && mode != null) {        if (mBackgroundTintInfo == null) {            mBackgroundTintInfo = new TintInfo();        }        mBackgroundTintInfo.mHasTintMode = true;        mBackgroundTintInfo.mTintMode = mode;    }}

3.3.3 setSupportBackgroundTint()

当设置backgroundTint的时候得到mBackgroundTintResId进行操作

private boolean setSupportBackgroundTint(int resId) {    if (resId != 0) {        if (mBackgroundTintInfo == null) {            mBackgroundTintInfo = new TintInfo();        }        mBackgroundTintInfo.mHasTintList = true;        mBackgroundTintInfo.mTintList = mTintManager.getColorStateList(resId);    }    return applySupportBackgroundTint();//[3.3.4]}

applySupportBackgroundTint()

private boolean applySupportBackgroundTint() {    Drawable backgroundDrawable = mView.getBackground();    if (backgroundDrawable != null && mBackgroundTintInfo != null && mBackgroundTintInfo.mHasTintList) {        backgroundDrawable = DrawableCompat.wrap(backgroundDrawable);        backgroundDrawable = backgroundDrawable.mutate();//起到单一修改背景的作用,并不会影响其他控件的色        if (mBackgroundTintInfo.mHasTintList) {            //对背景设置颜色            DrawableCompat.setTintList(backgroundDrawable, mBackgroundTintInfo.mTintList);        }        if (mBackgroundTintInfo.mHasTintMode) {            //还可以根据view不同的状态进行着色            DrawableCompat.setTintMode(backgroundDrawable, mBackgroundTintInfo.mTintMode);        }        //看是否backgroundDrawable进行改变        if (backgroundDrawable.isStateful()) {            backgroundDrawable.setState(mView.getDrawableState());        }        setBackgroundDrawable(backgroundDrawable);        return true;    }    return false;}

这里参考一片文章:

我们通过项目再来举个栗子

  1. 得到一个Item中的条目
public static class ViewHolderHeader extends ViewHolder {
public static ViewHolderHeader create(ViewGroup parent) { return new ViewHolderHeader(LayoutInflater.from( parent.getContext()).inflate(R.layout.layout_list_item_header, parent, false)); }}
  1. layout_list_item_header.xml
  • 其中scaleType:
    以原图完全显示为目的,将图片的内容完整居中显示,通过按比例缩小原图的size宽(高)等于或小于ImageView的宽(高)
  • 对应:imageTint<color name="theme_color_primary">#fb7299</color>

public class TintImageView extends ImageView implements Tintable, AppCompatBackgroundHelper.BackgroundExtensible,        AppCompatImageHelper.ImageExtensible {
public TintImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { return; } //生成TintManager TintManager tintManager = TintManager.get(context); //记录当前View与TintManager mBackgroundHelper = new AppCompatBackgroundHelper(this, tintManager);//[4.1] mBackgroundHelper.loadFromAttribute(attrs, defStyleAttr);//[4.2] mImageHelper = new AppCompatImageHelper(this, tintManager);//[4.3] mImageHelper.loadFromAttribute(attrs, defStyleAttr);//[4.4] } ... }

其中4.1和4.3都是记录作用就不多说

4.2 AppCompatBackgroundHelper.loadFromAttribute()

这里支持通过backgroundTintMode设置mode

void loadFromAttribute(AttributeSet attrs, int defStyleAttr) {    initPadding();//获取padding    TypedArray array = mView.getContext().obtainStyledAttributes(attrs, R.styleable.TintViewBackgroundHelper, defStyleAttr, 0);    if (array.hasValue(R.styleable.TintViewBackgroundHelper_backgroundTint)) {        //得到backgroundTint属性的值        mBackgroundTintResId = array.getResourceId(R.styleable.TintViewBackgroundHelper_backgroundTint, 0);        if (array.hasValue(R.styleable.TintViewBackgroundHelper_backgroundTintMode)) {            //得到backgroundTintMode的值并set,然后通过setSupportBackgroundTintMode设置进去            setSupportBackgroundTintMode(DrawableUtils.parseTintMode(array.getInt(R.styleable.TintViewBackgroundHelper_backgroundTintMode, 0), null));        }        setSupportBackgroundTint(mBackgroundTintResId);    } else {        Drawable drawable = mTintManager.getDrawable(mBackgroundResId = array.getResourceId(R.styleable.TintViewBackgroundHelper_android_background, 0));        if (drawable != null) {            setBackgroundDrawable(drawable);        }    }    array.recycle();}

首先用TintInfo记录,

private boolean setSupportBackgroundTint(int resId) {    if (resId != 0) {        if (mBackgroundTintInfo == null) {            mBackgroundTintInfo = new TintInfo();        }        mBackgroundTintInfo.mHasTintList = true;        mBackgroundTintInfo.mTintList = mTintManager.getColorStateList(resId);//是上一步配置的backgroundTint    }    return applySupportBackgroundTint();//[4.2.1]}

4.2.1 applySupportBackgroundTint()

private boolean applySupportBackgroundTint() {    Drawable backgroundDrawable = mView.getBackground();    if (backgroundDrawable != null && mBackgroundTintInfo != null && mBackgroundTintInfo.mHasTintList) {        //包装以后可进行着色        backgroundDrawable = DrawableCompat.wrap(backgroundDrawable);        backgroundDrawable = backgroundDrawable.mutate();//起到单一修改背景的作用,并不会影响其他控件的色        if (mBackgroundTintInfo.mHasTintList) {            //对背景设置颜色            DrawableCompat.setTintList(backgroundDrawable, mBackgroundTintInfo.mTintList);        }        if (mBackgroundTintInfo.mHasTintMode) {            //还可以根据view不同的状态进行着色            DrawableCompat.setTintMode(backgroundDrawable, mBackgroundTintInfo.mTintMode);        }        if (backgroundDrawable.isStateful()) {            backgroundDrawable.setState(mView.getDrawableState());        }        setBackgroundDrawable(backgroundDrawable);        return true;    }    return false;}

此时背景色已经设置完成

我们再来回顾一下,到底从哪里开始执行变色

private static void refreshView(View view, ExtraRefreshable extraRefreshable) {    if (view == null) return;    view.destroyDrawingCache();    if (view instanceof Tintable) {        ((Tintable) view).tint();//看这里

这个方法我们可以查看上面的回顾

TintImageView

@Overridepublic void tint() {    if (mBackgroundHelper != null) {        mBackgroundHelper.tint();//看一个就好    }    if (mImageHelper != null) {        mImageHelper.tint();    }}
@Overridepublic void tint() {    if (mBackgroundTintResId == 0 || !setSupportBackgroundTint(mBackgroundTintResId)) {        Drawable drawable = mTintManager.getDrawable(mBackgroundResId);//思想是根据mBackgroundResId得到Drawable        if (drawable == null) {            drawable = mBackgroundResId == 0 ? null : ContextCompat.getDrawable(mView.getContext(), mBackgroundResId);        }        setBackgroundDrawable(drawable);//设置背景    }}

最后我们就明白如何更换主题,我们主要是利用自定义控件,然后通过统一的设置进行更改。

根据项目结构进行大致分析

  • drawables
FilterableStateListDrawable
  • utils
ColorStateListUtilsDrawableInflateDelegateDrawableUtilsGradientDrawableInflateImplInputConnectionImplLayerDrawableInflateImplRippleDrawableInflateImplStateListDrawableInflateImplThemeUtilsTintInfoTintManagerVectorDrawableInflateImpl
  • widgets
AppCompatBackgroundHelperAppCompatBaseHelperAppCompatCompoundButtonHelperAppCompatCompoundDrawableHelperAppCompatForegroundHelperAppCompatImageHelperAppCompatProgressBarHelperAppCompatSwitchHelperAppCompatTextHelperTintableTintAppAlertDialogDividingViewTintAppBarLayoutTintAutoCompleteTextViewTintButtonTintCheckBoxTintCheckedTextViewTintConstraintLayoutTintEditTextTintFrameLayoutTintGridLayoutTintImageViewTintLinearLayoutTintProgressBarTintProgressDialogTintRadioButtonTintRelativeLayoutTintSwitchCompatTintTextViewTintToolbarTintView

FilterableStateListDrawable

这个类是针对StateListDrawable不能针对单一的状态添加colorFilter设计的

public class FilterableStateListDrawable extends StateListDrawable {
private int currIdx = -1; private int childrenCount = 0; private SparseArray
filterMap; public FilterableStateListDrawable() { super(); filterMap = new SparseArray<>(); } @Override public void addState(int[] stateSet, Drawable drawable) { super.addState(stateSet, drawable); childrenCount++; } public void addState(int[] stateSet, Drawable drawable, ColorFilter colorFilter) { if (colorFilter == null) { addState(stateSet, drawable); return; } // this is a new custom method, does not exist in parent class int currChild = childrenCount; addState(stateSet, drawable); filterMap.put(currChild, colorFilter); } @Override public boolean selectDrawable(int idx) { boolean result = super.selectDrawable(idx); // check if the drawable has been actually changed to the one I expect if (getCurrent() != null) { currIdx = result ? idx : currIdx; setColorFilter(getColorFilterForIdx(currIdx)); } else { currIdx = -1; setColorFilter(null); } return result; } private ColorFilter getColorFilterForIdx(int idx) { return filterMap != null ? filterMap.get(idx) : null; } @Override public ConstantState getConstantState() { return super.getConstantState(); }}

根据一个SparseArray这样就对应每一种状态有一个ColorFilter了。

ColorFilter主要用来处理颜色

- ColorMatrixColorFilter
- LightingColorFilter
- PorterDuffColorFilter

反正都是对图片颜色进行处理

再来看看ColorStateList的作用

/**  * 对TextView设置ColorStateList使其在Normal、Pressed、Focused、Unable四种状态下显示不同的颜色。
* StateListDrawable可直接使用图片应用在相似场合。 */ public class ActColorStateList extends Activity implements OnClickListener {
private TextView txtShow; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); txtShow = (TextView) findViewById(R.id.txtShow); txtShow.setText("Sodino\nNormal:0xffffffff\nPressed:0xffffff00\nFocused:0xff0000ff\nUnable:0xffff0000"); txtShow.setTextColor(createColorStateList(0xffffffff, 0xffffff00, 0xff0000ff, 0xffff0000)); txtShow.setOnClickListener(this); } /** 对TextView设置不同状态时其文字颜色。 */ private ColorStateList createColorStateList(int normal, int pressed, int focused, int unable) { int[] colors = new int[] { pressed, focused, normal, focused, unable, normal }; int[][] states = new int[6][]; states[0] = new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled }; states[1] = new int[] { android.R.attr.state_enabled, android.R.attr.state_focused }; states[2] = new int[] { android.R.attr.state_enabled }; states[3] = new int[] { android.R.attr.state_focused }; states[4] = new int[] { android.R.attr.state_window_focused }; states[5] = new int[] {}; ColorStateList colorList = new ColorStateList(states, colors); return colorList; } /** 设置Selector。 */ public static StateListDrawable newSelector(Context context, int idNormal, int idPressed, int idFocused, int idUnable) { StateListDrawable bg = new StateListDrawable(); Drawable normal = idNormal == -1 ? null : context.getResources().getDrawable(idNormal); Drawable pressed = idPressed == -1 ? null : context.getResources().getDrawable(idPressed); Drawable focused = idFocused == -1 ? null : context.getResources().getDrawable(idFocused); Drawable unable = idUnable == -1 ? null : context.getResources().getDrawable(idUnable); // View.PRESSED_ENABLED_STATE_SET bg.addState(new int[] { android.R.attr.state_pressed, android.R.attr.state_enabled }, pressed); // View.ENABLED_FOCUSED_STATE_SET bg.addState(new int[] { android.R.attr.state_enabled, android.R.attr.state_focused }, focused); // View.ENABLED_STATE_SET bg.addState(new int[] { android.R.attr.state_enabled }, normal); // View.FOCUSED_STATE_SET bg.addState(new int[] { android.R.attr.state_focused }, focused); // View.WINDOW_FOCUSED_STATE_SET bg.addState(new int[] { android.R.attr.state_window_focused }, unable); // View.EMPTY_STATE_SET bg.addState(new int[] {}, normal); return bg; } @Override public void onClick(View v) { if (v == txtShow) { txtShow.setEnabled(false); } } }

具体使用方法请

欢迎关注公众号:码老板

这里写图片描述

你可能感兴趣的文章
linux进程之间通讯常用信号
查看>>
main函数带参数
查看>>
PCB布线技巧
查看>>
关于PCB设计中过孔能否打在焊盘上的两种观点
查看>>
PCB反推理念
查看>>
京东技术架构(一)构建亿级前端读服务
查看>>
git 提示:error: unable to rewind rpc post data - try increasing http.postBuffer
查看>>
php 解决json_encode中文UNICODE转码问题
查看>>
LNMP 安装 thinkcmf提示404not found
查看>>
PHP empty、isset、innull的区别
查看>>
apache+nginx 实现动静分离
查看>>
通过Navicat远程连接MySQL配置
查看>>
phpstorm开发工具的设置用法
查看>>
Linux 系统挂载数据盘
查看>>
Git基础(三)--常见错误及解决方案
查看>>
Git(四) - 分支管理
查看>>
PHP Curl发送数据
查看>>
HTTP协议
查看>>
HTTPS
查看>>
git add . git add -u git add -A区别
查看>>