首先来介绍一下这个自定义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导航栏的更多相关文章

  1. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  2. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

  3. Android 自定义 View 圆形进度条总结

    Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...

  4. Android 自定义 View 详解

    View 的绘制系列文章: Android View 绘制流程之 DecorView 与 ViewRootImpl Android View 的绘制流程之 Measure 过程详解 (一) Andro ...

  5. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  6. (转)[原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  7. Android 自定义View (五)——实践

    前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...

  8. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  9. Android 自定义view(二) —— attr 使用

    前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...

随机推荐

  1. 使用Magicodes.SwaggerUI快速配置SwaggerUI以及设置API分组

    Magicodes.SwaggerUI 快速配置和集成SwaggerUI 特点 通过配置文件简单配置即可完成SwaggerUI的API格式JSON生成和集成 支持API分组和隐藏 支持自定义页面和验证 ...

  2. linux lsof 查看进程打开那些文件 或者 查看文件给那个进程使用

    lsof命令是什么? 可以列出被进程所打开的文件的信息.被打开的文件可以是 1.普通的文件,2.目录  3.网络文件系统的文件,4.字符设备文件  5.(函数)共享库  6.管道,命名管道 7.符号链 ...

  3. C Primer Plus (一)

    摘要:重读C Primer Plus ,查漏补缺 重读C Primer Plus,记录遗漏的.未掌握的.不清楚的知识点. 一.概览 1.链接器的作用是将这3个目标元素(目标代码.系统的标准启动代码和库 ...

  4. 实现ARM——Linux的自动登录

    在使用Linux系统嵌入式开发时,往往需要设备绕过Linux的登录系统使其自动启动,比如我们常用的SSH客户端等.网上确实有很多方法,不知道是因为我们的ARM9板子是私人订制的缘故还是什么原因,试了很 ...

  5. NOIP模拟 32

    我在31反思中膜拜过了B哥 没想到这次又... 我给老姚家丢脸了...STO 首先T1暴力就写挂了... 贪图从$n^3$*$2^n$优化成$n^2$*$2^n$然后打错了 哗哗的扔分 而且正解都想不 ...

  6. 用Vsftpd服务传输文件(铺垫篇)

    文件传输协议 文件传输协议(FTP,File Transfer Protocol),即能够让用户在互联网中上传.下载文件的文件协议,而FTP服务器就是支持FTP传输协议的主机,要想完成文件传输则需要F ...

  7. Python基本数据结构之集合

    一道python面试的一个小问题,说怎么使用一行代码将一个列表里的重复元素,其实这里只要将列表转换成集合就可以了. 定义 集合跟我们学的列表有点像,也是可以存一堆数据,不过它有几个独特的特点,令其在整 ...

  8. AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层

    AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层 AutoCad 二次开发 .net 之层表的增加 删除 修改图层颜色 遍历 设置当前层我理解的图层的作用大概是把 ...

  9. NOIP模拟26

    把题解沽了好久了,今天还是不想写,我们靠的B卷其实挺水的,但是我就是想吐槽一下!咋还带题目里面放题解的?题里一点题解的线索都没有,但是玄机竟然在题目里! 我也是醉了,T1就是一个贪心,题目说贪婪,T2 ...

  10. 由浅入深——从ArrayList浅谈并发容器

    原创作品转载请附:https://www.cnblogs.com/superlsj/p/11655523.html 一.一个案例引发的思考 public class ArrayListTest { p ...