【Android - 自定义View】之自定义颜色渐变的Tab导航栏
首先来介绍一下这个自定义View:
- (1)这个自定义View的名称叫做 GradientTab ,继承自View类;
- (2)这个自定义View实现了颜色渐变的Tab导航栏(仿微信主菜单),用户在左右滑动的时候,当前页对应的Tab逐渐变淡,目标页的Tab逐渐变深;
- (3)用户可以在XML布局中自定义变色的颜色、图标、文本、文本大小、文本颜色、图文间隔等属性。
接下来简单介绍一下在这个自定义View中用到的技术点:
- (1)自定义属性;
- (2)在 onMeasure() 方法中对View进行测量;
- (3)在 onLayout() 方法中进行一些在获取到测量值之后才能进行的操作,避免代码多次调用影响性能;
- (4)使用 Canvas 、 Paint 、 Bitmap 类对自定义View进行绘制;
- (5)通过 onSaveInstanceState() 和 onRestoreInstanceState() 方法保存和重置View状态(透明度)。
下面是这个自定义View—— GradientTab 的实现代码:
自定义View类 GradientTab.java 中的代码:
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.PorterDuff;
- import android.graphics.PorterDuffXfermode;
- import android.os.Bundle;
- import android.os.Parcelable;
- import android.support.annotation.Nullable;
- import android.util.AttributeSet;
- import android.util.TypedValue;
- import android.view.View;
- /**
- * 与ViewPager连用的拖动可渐变色的Tab按钮
- */
- public class GradientTab extends View {
- private int width, height; // View最终显示的宽高
- private int backColor = Color.GREEN; // 自定义属性:Tab按钮的背景颜色
- private int spacing = -1; // 自定义属性:Tab按钮中图标和文本的间隔
- private StringBuffer text = new StringBuffer(); // 自定义属性:Tab按钮中显示的文本
- private int textColor = Color.BLACK; // 自定义属性:Tab按钮中文本的颜色
- private int textSize = -1; // 自定义属性:Tab按钮的文本大小
- private Bitmap tabBitmap; // 绘制元素的Bitmap
- private Bitmap tmpBm; // 临时Bitmap
- private Paint tabPaint; // 绘制元素的画笔
- private Paint iconPaint; // 绘制图标的画笔
- private Paint textPaint; // 绘制文本的画笔
- private Bitmap iconBm; // 图标图片对应的Bitmap
- private float iconWidth; // 图标图片的宽度
- private float iconHeight; // 图标图片的高度
- private int alpha = 0; // 透明度
- public GradientTab(Context context) {
- this(context, null);
- }
- public GradientTab(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public GradientTab(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- // 加载自定义属性
- TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GradientTab, defStyleAttr, 0);
- int count = array.getIndexCount();
- for (int i = 0; i < count; i++) {
- int attr = array.getIndex(i);
- switch (attr) {
- case R.styleable.GradientTab_backColor:
- backColor = array.getColor(attr, Color.GREEN);
- break;
- case R.styleable.GradientTab_iconImg:
- int iconRes = array.getResourceId(attr, -1); // 自定义属性:Tab按钮中显示的图标的资源ID
- if (iconRes != -1) {
- iconBm = BitmapFactory.decodeResource(getResources(), iconRes);
- iconWidth = iconBm.getWidth();
- iconHeight = iconBm.getHeight();
- }
- break;
- case R.styleable.GradientTab_spacing:
- textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics())),
- context.getResources().getDisplayMetrics());
- break;
- case R.styleable.GradientTab_text:
- text.delete(0, text.length());
- text.append(array.getString(attr));
- break;
- case R.styleable.GradientTab_textColor:
- textColor = array.getColor(attr, Color.BLACK);
- break;
- case R.styleable.GradientTab_textSize:
- textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics())),
- context.getResources().getDisplayMetrics());
- break;
- }
- }
- array.recycle();
- // 为一些自定义属性设置默认值
- if (textSize == -1) {
- textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics());
- }
- if (spacing == -1) {
- spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics());
- }
- // 进行一些对象的初始化操作
- init();
- }
- /**
- * 进行一些对象的初始化操作
- */
- private void init() {
- // 初始化绘制图标的画笔
- iconPaint = new Paint();
- iconPaint.setAntiAlias(true);
- iconPaint.setDither(true);
- // 初始化绘制文本的画笔
- textPaint = new Paint();
- textPaint.setAntiAlias(true);
- textPaint.setDither(true);
- textPaint.setColor(textColor);
- textPaint.setTextSize(textSize);
- // 初始化绘制元素的画笔
- tabPaint = new Paint();
- tabPaint.setAntiAlias(true);
- tabPaint.setDither(true);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- width = getMeasuredWidth();
- height = getMeasuredHeight();
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // 初始化绘制元素的画布
- tabBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas tabCanvas = new Canvas(tabBitmap);
- // 对图标进行缩放
- int widthLeft = width - getPaddingLeft() - getPaddingRight();
- int heightLeft = height - getPaddingTop() - getPaddingBottom() - textSize - spacing;
- float scale = (float) Math.min(widthLeft * 1.0 / iconWidth, heightLeft * 1.0 / iconHeight);
- Matrix matrix = new Matrix();
- matrix.postScale(scale, scale);
- iconBm = Bitmap.createBitmap(iconBm, 0, 0, (int) iconWidth, (int) iconHeight, matrix, true);
- iconWidth *= scale;
- iconHeight *= scale;
- // 将图标和文本绘制到绘制元素的画布上
- tabCanvas.drawBitmap(iconBm, (widthLeft - iconWidth) / 2, getPaddingTop(), iconPaint);
- int textWidth = (int) textPaint.measureText(text.toString());
- tabCanvas.drawText(text.toString(), (widthLeft - textWidth) / 2, height - getPaddingBottom(), textPaint);
- tmpBm = tabBitmap.copy(Bitmap.Config.ARGB_8888, false);
- tabPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- tabPaint.setColor(backColor);
- tabCanvas.drawRect(0, 0, width, height, tabPaint);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawBitmap(tmpBm, 0, 0, null);
- // 绘制图标和文本
- Paint paint = new Paint();
- paint.setAlpha(alpha);
- canvas.drawBitmap(tabBitmap, 0, 0, paint);
- }
- public void setAlpha(float alpha) {
- this.alpha = (int) Math.ceil(255 * alpha);
- invalidate();
- }
- /**
- * 当这个View所在的Activity临时结束时,保存当前状态(透明度)
- */
- @Override
- protected Parcelable onSaveInstanceState() {
- Bundle bundle = new Bundle();
- bundle.putParcelable("default", super.onSaveInstanceState());
- bundle.putInt("alpha", alpha);
- return bundle;
- }
- /**
- * 当这个View所在的Activity重新打开时,重置当前状态(透明度)
- */
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- if (state instanceof Bundle) {
- Bundle bundle = (Bundle) state;
- alpha = bundle.getInt("alpha");
- super.onRestoreInstanceState(bundle.getParcelable("default"));
- }
- super.onRestoreInstanceState(state);
- }
- }
自定义属性文件 attr.xml 中的代码:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <attr name="backColor" format="color" />
- <attr name="iconImg" format="reference" />
- <attr name="spacing" format="dimension" />
- <attr name="text" format="string" />
- <attr name="textColor" format="color" />
- <attr name="textSize" format="dimension" />
- <declare-styleable name="GradientTab">
- <attr name="backColor" />
- <attr name="iconImg" />
- <attr name="spacing" />
- <attr name="text" />
- <attr name="textColor" />
- <attr name="textSize" />
- </declare-styleable>
- </resources>
主界面布局 activity_main.xml 文件中的代码:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout 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">
- <LinearLayout
- android:id="@+id/gradienttab_main_ly_tabs"
- android:layout_width="match_parent"
- android:layout_height="60.0dip"
- android:layout_alignParentBottom="true"
- android:background="#EEEEEE"
- android:orientation="horizontal">
- <my.itgungnir.custom_gradientmenu.GradientTab
- android:id="@+id/gradienttab_main_tab_tab1"
- android:layout_width="0.0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:padding="3.0dip"
- app:backColor="#008800"
- app:iconImg="@mipmap/menu_tab1"
- app:spacing="2.0dip"
- app:text="Tab1"
- app:textColor="#888888"
- app:textSize="8.0sp" />
- <my.itgungnir.custom_gradientmenu.GradientTab
- android:id="@+id/gradienttab_main_tab_tab2"
- android:layout_width="0.0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:padding="3.0dip"
- app:backColor="#880000"
- app:iconImg="@mipmap/menu_tab2"
- app:spacing="2.0dip"
- app:text="Tab2"
- app:textColor="#888888"
- app:textSize="8.0sp" />
- <my.itgungnir.custom_gradientmenu.GradientTab
- android:id="@+id/gradienttab_main_tab_tab3"
- android:layout_width="0.0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:padding="3.0dip"
- app:backColor="#000088"
- app:iconImg="@mipmap/menu_tab3"
- app:spacing="2.0dip"
- app:text="Tab3"
- app:textColor="#888888"
- app:textSize="8.0sp" />
- <my.itgungnir.custom_gradientmenu.GradientTab
- android:id="@+id/gradienttab_main_tab_tab4"
- android:layout_width="0.0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:padding="3.0dip"
- app:backColor="#888888"
- app:iconImg="@mipmap/menu_tab4"
- app:spacing="2.0dip"
- app:text="Tab4"
- app:textColor="#888888"
- app:textSize="8.0sp" />
- </LinearLayout>
- <android.support.v4.view.ViewPager
- android:id="@+id/gradienttab_main_vp_pages"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@id/gradienttab_main_ly_tabs" />
- </RelativeLayout>
主界面JAVA文件 MainActivity.java 中的代码:
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentActivity;
- import android.support.v4.app.FragmentPagerAdapter;
- import android.support.v4.view.ViewPager;
- import java.util.ArrayList;
- import java.util.List;
- public class MainActivity extends FragmentActivity {
- private ViewPager viewpager;
- private List<GradientTab> tabList;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
- @Override
- protected void onResume() {
- super.onResume();
- viewpager = (ViewPager) findViewById(R.id.gradienttab_main_vp_pages);
- initData();
- initEvents();
- }
- private void initData() {
- // 初始化盛放Fragment的列表
- final List<Fragment> list = new ArrayList<>();
- for (int i = 1; i <= 4; i++) {
- PageFragment fragment = new PageFragment();
- Bundle bundle = new Bundle();
- bundle.putString("title", "This is page " + i);
- fragment.setArguments(bundle);
- list.add(fragment);
- }
- // 为ViewPager适配数据
- viewpager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
- @Override
- public Fragment getItem(int position) {
- return list.get(position);
- }
- @Override
- public int getCount() {
- return list.size();
- }
- });
- // 初始化四个Tab按钮,添加到List列表中
- tabList = new ArrayList<>();
- tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab1));
- tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab2));
- tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab3));
- tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab4));
- }
- private void initEvents() {
- // ViewPager的滚动事件
- viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- tabList.get(position).setAlpha(1 - positionOffset);
- tabList.get((int) Math.ceil(position + positionOffset)).setAlpha(positionOffset == 0 ? 1 : positionOffset);
- }
- @Override
- public void onPageSelected(int position) {
- }
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
- }
- }
运行效果图如下所示:
【Android - 自定义View】之自定义颜色渐变的Tab导航栏的更多相关文章
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
- Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...
- Android 自定义 View 详解
View 的绘制系列文章: Android View 绘制流程之 DecorView 与 ViewRootImpl Android View 的绘制流程之 Measure 过程详解 (一) Andro ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
- (转)[原] Android 自定义View 密码框 例子
遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...
- Android 自定义View (五)——实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
- Android 自定义 view(四)—— onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
- Android 自定义view(二) —— attr 使用
前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...
随机推荐
- http和Https简介、详解
目录 引用 一.HTTP和HTTPS的基本概念 二.HTTP与HTTPS有什么区别? 三.HTTPS的工作原理 四.HTTPS的优点 五.HTTPS的缺点 六.http切换到HTTPS 引用 超文本传 ...
- JVM内存结构、参数调优和内存泄露分析
1. JVM内存区域和参数配置 1.1 JVM内存结构 Java堆(Heap) Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都 ...
- Bash 内置高效特性
变量(字符串)变换 定义一个变量t,内容为framE [root@vm1 tmp]# t=framE 查看变量t的内容:echo $t或者是echo ${t} [root@vm1 tmp]# echo ...
- Web for pentester_writeup之XML attacks篇
Web for pentester_writeup之XML attacks篇 XML attacks(XML攻击) Example 1 - XML外部实体注入(XXE) Payload http:// ...
- git 命令归纳版
1.克隆: 单纯的克隆名字: git clone [url] 自定义新建项目名称: git clone [url] [项目名字] 2.跟踪文件: git add [文件名] 3.添加忽略文件 ...
- 使用float设置经典的网站前端结构(深入探讨)
.要是DIV的子元素宽度大于它自己的宽度,不管子元素有没有脱离文档流,子元素会在横向向右溢出. 关于高度:1.要是DIV的高度没有设定,其高度受“没有脱离文档流”的子元素影响.以下是DIV宽度为0的情 ...
- 使用 Github + Hexo 从 0 搭建一个博客
最近有几位同学在公众号后台留言问我的博客站是怎么建站的,思来想去,还是写一篇从 0 开始吧. 前置准备 我们先聊一下前置准备,可能很多同学一听说要自己搭一个博客系统,直接就望而却步.不得有台服务器么, ...
- 关于 Java 中多线程的面试问题 详解
多线程细节: 1. 面试题: sleep 方法 和 wait 方法异同点是什么? 相同点: 可以让线程 处于 冻结状态. 不同点: 1. sleep 必须指定时间 wait 可以指定时间, 也可以不指 ...
- CSPS模拟 78
大敛好稳啊..居然在模拟赛拿了540.. 有点畏惧.jpg 而我就是什么什么不行级人物了.. 真正在联赛拉开那么多分怎么追啊.. T1kmp?hash? T2 概率小到炸精时,对答案也就没贡献了 然后 ...
- 项目——基于httpd镜像演示Dockerfile所有的指令
基于httpd镜像演示Dockerfile所有的指令: 第一步:创建Dockerfile工作目录 [root@localhost harbor]# mkdir /test [root@localhos ...