android:id=“@+id/tab_title"的TextView显示标题。android:id=”@+id/tab_image"的ImageView显示的是一个轮廓图片,也就是未选中时候的图片。android:id=“@+id/tab_image_top"的ImageView在android:id=”@+id/tab_image"的ImageView之上,显示的是选中时候的图片。轮廓图片和选中的图片,可以看下如下的图片例子
第一图片就是轮廓图片,第二个图片就是选中后的图片。为什么布局要这么设计呢?这当然是根据微信的动画而设计的布局(废话!),首先默认显示的是轮廓图片,当接收到一个进度值后,会让轮廓图片的轮廓变色,当进度值超过某个阈值的时候,让轮廓图片的透明度渐渐变为0(也就是完全透明,看不见),而让选中的图片的透明度渐渐变为255(也就是慢慢变清晰)。
TabView
既然知道了变色的原理,现在就来写TabView的代码吧。首先为TabView抽取自定义属性
// res/values/tabview_attrs.xml
<?xml version="1.0" encoding="utf-8"?>-
tabColor代表变色最终显示的颜色。
-
tabImage代表默认显示的轮廓图。
-
tabSelectedImage代表选中后的图。
-
tabTitle代表要显示的标题。
然后,在TabView中加载布局,并且获取自定义属性
public TabView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 加载布局
inflate(context, R.layout.tab_layout, this);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabView);
for (int i = 0; i < a.getIndexCount(); i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.TabView_tabColor:
// 获取标题和轮廓最终的着色
mTargetColor = a.getColor(attr, DEFAULT_TAB_TARGET_COLOR);
break;
case R.styleable.TabView_tabImage:
// 获取轮廓图
mNormalDrawable = a.getDrawable(attr);
break;
case R.styleable.TabView_tabSelectedImage:
// 获取选中图
mSelectedDrawable = a.getDrawable(attr);
break;
case R.styleable.TabView_tabTitle:
// 获取标题
mTitle = a.getString(attr);
break;
}
}
a.recycle();
}
当加载完布局后,需要为各个控件设置相应的属性
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 1.设置标题,默认着色为黑色
mTitleView = findViewById(R.id.tab_title);
mTitleView.setTextColor(DEFAULT_TAB_COLOR);
mTitleView.setText(mTitle);
// 2.设置轮廓图片,不透明,默认着色为黑色
mNormalImageView = findViewById(R.id.tab_image);
mNormalDrawable.setTint(DEFAULT_TAB_COLOR);
mNormalDrawable.setAlpha(255);
mNormalImageView.setImageDrawable(mNormalDrawable);
// 3.设置选中图片,透明,默认着色为黑色
mSelectedImageView = findViewById(R.id.tab_selected_image);
mSelectedDrawable.setAlpha(0);
mSelectedImageView.setImageDrawable(mSelectedDrawable);
}
第二步中,为轮廓图调用了Drawable.setTint()方法为轮廓着色,默认着色为黑色。
Drawable.setTint() 其实就是利用 PorterDuff.Mode.DST_IN 来进行颜色混合。
然后我还需要一个接收进度值(范围为0.f 到 1.f)的方法,从而利用这个进度值做动画
/**
-
根据进度值进行变色和透明度处理。
-
@param percentage 进度值,取值[0, 1]。
*/
public void setXPercentage(float percentage) {
if (percentage < 0 || percentage > 1) {
return;
}
// 1. 颜色变换
int finalColor = evaluate(percentage, DEFAULT_TAB_COLOR, mTargetColor);
mTitleView.setTextColor(finalColor);
mNormalDrawable.setTint(finalColor);
// 2. 透明度变换
if (percentage >= 0.5 && percentage <= 1) {
// 原理如下
// 进度值: 0.5 ~ 1
// 透明度: 0 ~ 1
// 公式: percentage - 1 = (alpha - 1) * 0.5
int alpha = (int) Math.ceil(255 * ((percentage - 1) * 2 + 1));
mNormalDrawable.setAlpha(255 - alpha);
mSelectedDrawable.setAlpha(alpha);
} else {
mNormalDrawable.setAlpha(255);
mSelectedDrawable.setAlpha(0);
}
// 3. 更新UI
invalidateUI();
}
第一步是根据进度值来计算颜色值。在属性动画中,有一个ArgbEvaluator类,这是一个对颜色做动画的类,它里面有一个方法如下
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0…255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round® << 16 | Math.round(g) << 8 | Math.round(b);
}
熟悉属性动画的应该知道,参数float fraction的取值范围为0到1,所以可以把这个方法拷贝过来使用。计算出颜色值后,就可以对标题和轮廓图着色了。第二步,按照之前说的动画原理,要利用进度值计算透明度,然后分别对轮廓图和选中图设置透明度。透明度的计算原理已经在注释中写清楚了,这里不再赘述。最后就是更新UI了。
与ViewPager联动
一切准备就绪,就等一个ViewPager的进度值
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
});
既然想知道ViewPager如何提供进度值,那就必须了解onPageScrolled方法的几个参数的意思,首先从源码的注释中进行了解
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/18a9f1ead3ad03f0dda35cab40cd940f.jpeg)
文末
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
/>
文末
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家
[外链图片转存中…(img-aCKpx330-1712443673660)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!