android 底部tab效果,Android 仿微信底部渐变Tab效果
先来看一下效果图
除了第三个的发现Tab有所差别外,其他的基本还原了微信的底部Tab渐变效果
每个Tab都是一个自定义View,根据ImageView的tint属性来实现颜色渐变效果,tint属性的使用可以看我的上一篇文章
我将自定义View命名为ShadeView,包含四个自定义属性
意思分别为图标、背景色、底部文本、底部文本大小
ShadeView的定义如下,主要是进行绘图操作,并向外提供改变透明度和图标的方法
public class ShadeView extends View {
/**
* 图标
*/
private Bitmap iconBitmap;
/**
* 图标背景色
*/
private int iconBackgroundColor;
/**
* 图标默认背景色
*/
private final int DEFAULT_ICON_BACKGROUND_COLOR = 0x3CAF36;
/**
* 图标底部文本
*/
private String text;
/**
* 图标底部文字默认大小(sp)
*/
private final int DEFAULT_TEXT_SIZE = 12;
/**
* 图标底部文字默认颜色
*/
private final int DEFAULT_TEXT_COLOR = 0x2B2B2B;
/**
* 图标绘制范围
*/
private Rect iconRect;
/**
* 文字笔画
*/
private Paint textPaint;
/**
* 文字范围
*/
private Rect textBound;
/**
* 透明度(0.0-1.0)
*/
private float mAlpha;
private Bitmap mBitmap;
public ShadeView(Context context, AttributeSet attrs) {
super(context, attrs);
//获取自定义属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShadeView);
BitmapDrawable drawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.ShadeView_icon);
if (drawable != null) {
iconBitmap = drawable.getBitmap();
}
iconBackgroundColor = typedArray.getColor(R.styleable.ShadeView_color, DEFAULT_ICON_BACKGROUND_COLOR);
text = typedArray.getString(R.styleable.ShadeView_text);
int textSize = (int) typedArray.getDimension(R.styleable.ShadeView_text_size,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE, getResources().getDisplayMetrics()));
//资源回收
typedArray.recycle();
//初始化
textBound = new Rect();
textPaint = new Paint();
textPaint.setTextSize(textSize);
textPaint.setColor(DEFAULT_TEXT_COLOR);
textPaint.setAntiAlias(true);
textPaint.setDither(true);
textPaint.getTextBounds(text, 0, text.length(), textBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//因为图标是正方形且需要居中显示的,所以View的大小去掉padding和文字所占空间后,
//剩余的空间的宽和高的最小值才是图标的边长
int bitmapSide = Math.min(getMeasuredWidth() - getPaddingLeft()
- getPaddingRight(), getMeasuredHeight() - getPaddingTop()
- getPaddingBottom() - textBound.height());
int left = getMeasuredWidth() / 2 - bitmapSide / 2;
int top = (getMeasuredHeight() - textBound.height()) / 2 - bitmapSide / 2;
//获取图标的绘制范围
iconRect = new Rect(left, top, left + bitmapSide, top + bitmapSide);
}
@Override
protected void onDraw(Canvas canvas) {
//进一取整
int alpha = (int) Math.ceil((255 * mAlpha));
//绘制原图标
canvas.drawBitmap(iconBitmap, null, iconRect, null);
setupTargetBitmap(alpha);
drawSourceText(canvas, alpha);
drawTargetText(canvas, alpha);
canvas.drawBitmap(mBitmap, 0, 0, null);
}
/**
* 在mBitmap上绘制以iconBackgroundColor颜色为Dst,DST_IN模式下的图标
*
* @param alpha Src颜色的透明度
*/
private void setupTargetBitmap(int alpha) {
mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmap);
Paint paint = new Paint();
paint.setColor(iconBackgroundColor);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setAlpha(alpha);
//在图标背后先绘制一层iconBackgroundColor颜色的背景
canvas.drawRect(iconRect, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
paint.setAlpha(255);
//在mBitmap上绘制以iconBackgroundColor颜色为Dst,DST_IN模式下的图标
canvas.drawBitmap(iconBitmap, null, iconRect, paint);
}
/**
* 绘制默认状态下的字体
*
* @param canvas Canvas
* @param alpha 字体颜色透明度
*/
private void drawSourceText(Canvas canvas, int alpha) {
textPaint.setColor(DEFAULT_TEXT_COLOR);
textPaint.setAlpha(255 - alpha);
canvas.drawText(text, iconRect.left + iconRect.width() / 2 - textBound.width() / 2,
iconRect.bottom + textBound.height(), textPaint);
}
/**
* 绘制滑动到该标签时的字体
*
* @param canvas Canvas
* @param alpha 字体颜色透明度
*/
private void drawTargetText(Canvas canvas, int alpha) {
textPaint.setColor(iconBackgroundColor);
textPaint.setAlpha(alpha);
canvas.drawText(text, iconRect.left + iconRect.width() / 2 - textBound.width() / 2,
iconRect.bottom + textBound.height(), textPaint);
}
/**
* 设置图标透明度并重绘
*
* @param alpha 透明度
*/
public void setIconAlpha(float alpha) {
if (mAlpha != alpha) {
this.mAlpha = alpha;
invalidateView();
}
}
public void setIconBitmap(Context context, int resourceID) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) ContextCompat.getDrawable(context, resourceID);
if (!bitmapDrawable.getBitmap().equals(iconBitmap)) {
iconBitmap = bitmapDrawable.getBitmap();
invalidateView();
}
}
/**
* 判断当前是否为UI线程,是则直接重绘,否则调用postInvalidate()利用Handler来重绘
*/
private void invalidateView() {
if (Looper.getMainLooper() == Looper.myLooper()) {
invalidate();
} else {
postInvalidate();
}
}
private static final String STATE_INSTANCE = "STATE_INSTANCE";
private static final String STATE_ALPHA = "STATE_ALPHA";
/**
* 保存状态
*
* @return Parcelable
*/
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putFloat(STATE_ALPHA, mAlpha);
return bundle;
}
/**
* 恢复状态
*
* @param parcelable Parcelable
*/
@Override
protected void onRestoreInstanceState(Parcelable parcelable) {
if (parcelable instanceof Bundle) {
Bundle bundle = (Bundle) parcelable;
mAlpha = bundle.getFloat(STATE_ALPHA);
super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));
} else {
super.onRestoreInstanceState(parcelable);
}
}
}
然后在布局文件中声明使用,这里不需要每个自定义属性都使用到,因为我也提供了默认值
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:id="@+id/id_viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@drawable/tab_background"
android:orientation="horizontal"
android:paddingBottom="3dp"
android:paddingTop="1dp">
android:id="@+id/id_indicator_one"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/weixin"
app:text="微信" />
android:id="@+id/id_indicator_two"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/address_book"
app:text="通讯录" />
android:id="@+id/id_indicator_three"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/discover"
app:text="发现" />
android:id="@+id/id_indicator_four"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:icon="@drawable/me"
app:text="我" />
因为主界面是ViewPager,这里就需要一个Fragment子类
public class TabFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String mTitle = "微信";
if (getArguments() != null) {
mTitle = getArguments().getString("Title", "微信");
}
TextView textView = new TextView(getActivity());
textView.setTextSize(25);
textView.setGravity(Gravity.CENTER);
textView.setText(mTitle);
return textView;
}
}
MainActivity代码如下,重点是对viewPager进行滑动监听,根据滑动偏移量来动态改变透明度alpha,从而实现颜色渐变效果
public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener, View.OnClickListener {
private List tabFragments;
private List tabIndicators;
private ViewPager viewPager;
private FragmentPagerAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
viewPager = (ViewPager) findViewById(R.id.id_viewpager);
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(this);
}
private void initData() {
tabFragments = new ArrayList<>();
tabIndicators = new ArrayList<>();
String[] titles = new String[]{"微信", "通讯录", "发现", "我"};
for (String title : titles) {
TabFragment tabFragment = new TabFragment();
Bundle bundle = new Bundle();
bundle.putString("Title", title);
tabFragment.setArguments(bundle);
tabFragments.add(tabFragment);
}
adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public int getCount() {
return tabFragments.size();
}
@Override
public Fragment getItem(int arg0) {
return tabFragments.get(arg0);
}
};
initTabIndicator();
}
private void initTabIndicator() {
ShadeView one = (ShadeView) findViewById(R.id.id_indicator_one);
ShadeView two = (ShadeView) findViewById(R.id.id_indicator_two);
ShadeView three = (ShadeView) findViewById(R.id.id_indicator_three);
ShadeView four = (ShadeView) findViewById(R.id.id_indicator_four);
tabIndicators.add(one);
tabIndicators.add(two);
tabIndicators.add(three);
tabIndicators.add(four);
one.setOnClickListener(this);
two.setOnClickListener(this);
three.setOnClickListener(this);
four.setOnClickListener(this);
one.setIconAlpha(1.0f);
}
@Override
public void onClick(View v) {
resetTabsStatus();
switch (v.getId()) {
case R.id.id_indicator_one:
tabIndicators.get(0).setIconAlpha(1.0f);
viewPager.setCurrentItem(0, false);
break;
case R.id.id_indicator_two:
tabIndicators.get(1).setIconAlpha(1.0f);
viewPager.setCurrentItem(1, false);
break;
case R.id.id_indicator_three:
tabIndicators.get(2).setIconAlpha(1.0f);
viewPager.setCurrentItem(2, false);
break;
case R.id.id_indicator_four:
tabIndicators.get(3).setIconAlpha(1.0f);
viewPager.setCurrentItem(3, false);
break;
}
}
/**
* 重置Tab状态
*/
private void resetTabsStatus() {
for (int i = 0; i < tabIndicators.size(); i++) {
tabIndicators.get(i).setIconAlpha(0);
}
}
/**
* 如果是直接点击图标来跳转页面的话,position值为0到3,positionOffset一直为0.0
* 如果是通过滑动来跳转页面的话
* 假如是从第一页滑动到第二页
* 在这个过程中,positionOffset从接近0逐渐增大到接近1.0,滑动完成后又恢复到0.0,而position只有在滑动完成后才从0变为1
* 假如是从第二页滑动到第一页
* 在这个过程中,positionOffset从接近1.0逐渐减小到0.0,而position一直是0
*
* @param position 当前页面索引
* @param positionOffset 偏移量
* @param positionOffsetPixels 偏移量
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e("TAG", "position==" + position);
Log.e("TAG", "positionOffset==" + positionOffset);
Log.e("TAG", "positionOffsetPixels==" + positionOffsetPixels);
if (positionOffset > 0) {
ShadeView leftTab = tabIndicators.get(position);
ShadeView rightTab = tabIndicators.get(position + 1);
leftTab.setIconAlpha(1 - positionOffset);
rightTab.setIconAlpha(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
if (position == 2) {
tabIndicators.get(position).setIconBitmap(this, R.drawable.discover_green);
} else {
tabIndicators.get(2).setIconBitmap(this, R.drawable.discover);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
总结
以上所述是小编给大家介绍的Android 仿微信底部渐变Tab效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
android 底部tab效果,Android 仿微信底部渐变Tab效果相关推荐
- Android仿微信底部菜单栏+今日头条顶部导航栏
背景 Android应用几乎都会用到底部菜单栏,在Material Design还没有出来之前,TabHost等技术一直占主流,现在Google新sdk中提供了TabLayout类可以便捷的做出底部菜 ...
- JFTabBar android强大的底部导航栏框架 (微信底部导航栏效果)
TabBar这个名字相信很多学过一点IOS程序员都知道它是用来干嘛的,但本人也并非擅长开发IOS程序员,只是略懂略懂....这是一个很强大的TabBar,可满足很多需求.用起来也非常简单,在oncre ...
- android bmob 朋友圈,仿微信朋友圈视频效果 – MVideo
MVideo 仿微信朋友圈视频效果,可以拖拽及缩放,视频查看,基于ijkplayer. Demo 入门 Step 1:在buil文件中添加JitPack仓库: allprojects { reposi ...
- Android 二维码扫描(仿微信界面),根据Google zxing
Android 二维码扫描(仿微信界面),根据Google zxing Android项目开发中经常会用到二维码扫描,例如登陆.支付等谷歌方面已经有了一个开源库(地址: https://github. ...
- Android 使用 CameraX 快速实现仿微信短视频录制
Android 使用 CameraX 快速实现仿微信短视频录制(轻触拍照.长按录像) https://github.com/ldlywt/MyCameraX 微信短视频android端 https:/ ...
- php仿微信底部菜单,Android实现简单底部导航栏 Android仿微信滑动切换效果
Android仿微信滑动切换最终实现效果: 大体思路: 1. 主要使用两个自定义View配合实现; 底部图标加文字为一个自定义view,底部导航栏为一个载体,根据需要来添加底部图标; 2. 底部导航栏 ...
- Android仿微信底部菜单栏+顶部菜单栏(附源码)
林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文要实现仿微信微信底部菜单栏+顶部菜单栏,采用ViewPage来做,每一个page对应一个X ...
- android高仿微信底部渐变导航栏
最近有很多人问微信底部的变色卡片导航是怎么做的,我在网上看了好几个例子,都是效果接近,都存有一些差异,自己琢磨也做了一个,几乎99%的还原,效果还不错吧 仔细观察微信图片,发现他有两部分内容,外面的边 ...
- android 微信底部渐变,仿微信底部导航栏BottomNavigation渐变色
ezgif.com-video-to-gif.gif 不说废话了,直接上代码: 自定义View /** * * author : Harvey * time : 2018/06/14 * desc : ...
最新文章
- Java编程入门(2.1):基础Java应用程序
- 【PP操作手册】工作中心的维护
- String对象的indexOf方法
- python的for语句有几种写法_Python if 和 for 的多种写法
- php验证百度云doc,百度云推送通知埋的大大的坑,成功测试REST API for PHP服务端...
- docker jib_Jib –为Spring Boot应用程序构建docker映像
- 三维网格精简算法java版_几种常见算法的精简版-
- 【方案分享】华为智慧农业解决方案.pdf(附下载链接)
- storm能不能测试wadl_测试网红燃油宝到底能不能除积碳,看完不花冤枉钱
- JavaScript就这么回事 (JS基础知识整理)
- c语言 sprintf_s 参数 通配符,Rust教程(翻译).doc
- HTML5 input新增属性
- 【UWA 学堂】部分渲染课程的调价通知
- 尚学堂马士兵Linux视频教程笔记
- sqliteman安装错误
- python制作动态二维码
- host文件的用途和用法
- linux 天文软件,Stellarium 0.18.0虚拟天文馆软件发布,支持HiPS
- Linux中pkg-config的使用
- ArcGIS Pro脚本工具(6)——修复CAD图层数据源