【Android源代码】Android自定义的TabBar

2024-06-06 03:18

本文主要是介绍【Android源代码】Android自定义的TabBar,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(更多Android开发源码请访问:eoe移动开发者社区 http://www.eoeandroid.com/

Android自定义的TabBar,自定义了几个属性,可以在xml布局文件中使用,Tab的标题、图标等属性可以在布局文件中完成配置,Java代码中只需要指定Tab跳转的监听接口。可以在所有Android版本使用,建议配合ViewPager使用。
完整的Demo工程已给出!

 

1. [文件]     attrs.xml ~ 520B    

01 <?xml version=“1.0” encoding=“utf-8”?> 
02 <resources> 
03     <declare-styleable name=“TabBar”> 
04         <attr name=“icons” format=“reference” /> 
05         <!-- icons是一个存放drawable资源文件名的数组 --> 
06         <attr name=“titles” format=“reference” /> 
07         <!-- titles是一个存放string的数组 --> 
08         <attr name=“IconAboveTitle” format=“boolean” /> 
09         <!-- 允许指定分隔线的drawable --> 
10         <attr name=“Seperator” format=“reference” /> 
11     </declare-styleable>  
12 </resources> 

2. [文件]     TabBar.java ~ 8KB    

001 package com.pupa.common.widget; 
002   
003 import com.pupa.common.util.StringHelper; 
004 import com.pupa.TabBarDemo.R; 
005   
006 import android.annotation.SuppressLint; 
007 import android.content.Context; 
008 import android.content.res.Resources; 
009 import android.content.res.TypedArray; 
010 import android.graphics.Bitmap; 
011 import android.graphics.BitmapFactory; 
012 import android.graphics.Canvas; 
013 import android.graphics.Color; 
014 import android.graphics.Paint; 
015 import android.graphics.Rect; 
016 import android.graphics.drawable.Drawable; 
017 import android.util.AttributeSet; 
018 import android.view.Display; 
019 import android.view.Gravity; 
020 import android.view.View; 
021 import android.view.WindowManager; 
022 import android.widget.ImageView; 
023 import android.widget.ImageView.ScaleType; 
024 import android.widget.LinearLayout; 
025 import android.widget.TextView; 
026   
027 public class TabBar extends LinearLayout { 
028     private static final String TAG = “TabBar”; 
029     private final TabBar mTabBar; // 自己 
030     private final Context mContext; 
031     private final AttributeSet mAttrs; 
032     private int mDefStyle; 
033   
034     private Paint mPaint; 
035     private int mCurrentTabMaskColor; 
036     private int mCurrentTabMaskAlpha; 
037   
038     private String mResPackageName; 
039     private CharSequence[] mTitles; 
040     private CharSequence[] mIconNames; 
041     private Drawable[] mIcons; 
042     private Drawable mSeperator; 
043     private int mSeperatorResId; 
044     private int mSeperatorWidth; 
045   
046     private boolean mIconAboveTitle; 
047   
048     private int mCurrentTabIndex; 
049     private int mTabCount; 
050     private int mTabWidth; 
051     private int mPosition; 
052     private OnCurrentTabChangedListener mTabChangedListener; 
053   
054     public static final int POSITION_TOP = 1; // 位于顶部或者底部 
055     public static final int POSITION_BOTTOM = 2; 
056   
057     public TabBar(Context context) { 
058         this(context, null); 
059         // TODO Auto-generated constructor stub 
060     } 
061   
062     public TabBar(Context context, AttributeSet attrs) { 
063         super(context, attrs); 
064         setWillNotDraw(false); // 重要!!! 
065         mContext = context; 
066         mAttrs = attrs; 
067         mCurrentTabIndex = -1; 
068         mTabCount = 0; 
069         mSeperatorWidth = 0; 
070         mPosition = POSITION_TOP; 
071         mCurrentTabMaskColor = Color.BLACK; 
072         mCurrentTabMaskAlpha = 0x5f; 
073         mPaint = new Paint(); 
074         mTabBar = this; 
075         init(); 
076         // TODO Auto-generated constructor stub 
077     } 
078       
079     @SuppressWarnings(“deprecation”) 
080     public void init() { 
081         getResourcesFromXml(); 
082         this.setOrientation(LinearLayout.HORIZONTAL); 
083         this.setPadding(0, 0, 0, 0); 
084   
085         WindowManager wm = (WindowManager) mContext 
086                 .getSystemService(Context.WINDOW_SERVICE); 
087         Display dp = wm.getDefaultDisplay(); 
088         mTabWidth = dp.getWidth(); 
089   
090         mTabCount = mTitles.length; 
091   
092         if (mTabCount > 0) { 
093             if (mSeperator != null) { 
094                 Bitmap bmp = BitmapFactory.decodeResource(getResources(), 
095                         mSeperatorResId); 
096                 mSeperatorWidth = bmp.getWidth(); 
097                 mTabWidth = mTabWidth - (mTabCount - 1) * mSeperatorWidth; 
098                 bmp.recycle(); 
099                 bmp = null; 
100             } 
101             mTabWidth = mTabWidth / mTabCount; // 计算每个tab的宽度 
102             mCurrentTabIndex = 0; 
103         } 
104   
105         LayoutParams inParams = new LayoutParams(LayoutParams.MATCH_PARENT, 
106                 LayoutParams.WRAP_CONTENT); 
107         LayoutParams outParams = new LayoutParams(mTabWidth, 
108                 LayoutParams.WRAP_CONTENT); 
109         LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 
110                 LayoutParams.WRAP_CONTENT); 
111         View.OnClickListener clkListener = new View.OnClickListener() { 
112   
113             @Override 
114             public void onClick(View v) { 
115                 // TODO Auto-generated method stub 
116                 int index = (Integer) v.getTag(); 
117                 if (index != mCurrentTabIndex) { 
118                     if (mTabChangedListener != null) 
119                         mTabChangedListener.onCurrentTabChanged(index); 
120                     mCurrentTabIndex = index; 
121                     mTabBar.invalidate(); 
122                 } 
123             } 
124         }; 
125           
126         //逐个添加Tab 
127         for (int i = 0; i < mTabCount; ++i) { 
128             LinearLayout tab = new LinearLayout(mContext); 
129             tab.setOrientation(LinearLayout.VERTICAL); 
130             tab.setPadding(0, 0, 0, 0); 
131             tab.setTag(i); // 设置内部标号 
132             tab.setClickable(true); 
133             ImageView imv = new ImageView(mContext); 
134             imv.setScaleType(ScaleType.CENTER); 
135             if (i < mIcons.length) 
136                 imv.setImageDrawable(mIcons[i]); 
137             TextView tv = new TextView(mContext); 
138             tv.setGravity(Gravity.CENTER_HORIZONTAL); 
139             tv.setText(mTitles[i]); 
140             if (mIconAboveTitle) { // 图标在标题之上 
141                 tab.addView(imv, inParams); 
142                 tab.addView(tv, inParams); 
143             } else { // 标题在图标之上 
144                 tab.addView(tv, inParams); 
145                 tab.addView(imv, inParams); 
146             } 
147             tab.setOnClickListener(clkListener); 
148             this.addView(tab, outParams); 
149             if (mSeperator != null && i < mTabCount - 1) { 
150                 ImageView sep = new ImageView(mContext); 
151                 sep.setImageDrawable(mSeperator); 
152                 this.addView(sep, params); 
153             } 
154         } 
155     } 
156   
157     /** 
158      * 设置当前Tab的序号 
159      *  
160      * @param index 
161      *            你想指定的Tab的序号 
162      */ 
163     public void setCurrentTab(int index) { 
164         if (index > -1 && index < mTabCount&&index!=mCurrentTabIndex) { 
165             mCurrentTabIndex = index; 
166             this.invalidate(); 
167             if (mTabChangedListener != null) 
168                 mTabChangedListener.onCurrentTabChanged(mCurrentTabIndex); 
169         } 
170     } 
171   
172     public void setOnCurrentTabChangedListener( 
173             OnCurrentTabChangedListener listener) { 
174         mTabChangedListener = listener; 
175     } 
176   
177     /** 
178      * 设置TabBar在顶端还是底端。真实位置由你的Activity的布局文件决定,这里仅仅是作一个标识, 根据这个信息可以增加一些自定义的效果 
179      *  
180      * @param i 
181      *            顶端TabBar.POSITION_TOP或底端TabBar.POSITION_BOTTOM 
182      */ 
183     public void setTabBarPosition(int i) { 
184         mPosition = i; 
185     } 
186   
187     /** 
188      * 设定工程中R.java文件的包名,因为在解析出各个Tab的icon时要用到。如果是默认值则无需指定 
189      *  
190      * @param name 
191      *            R.java文件的包名 
192      */ 
193     public void setResourcesPackageName(String name) { 
194         mResPackageName = name; 
195     } 
196   
197     /** 
198      * 设置Tab选中后的颜色,默认alpha为0x5f 
199      *  
200      * @param c 
201      *            rgb颜色值 
202      */ 
203     public void setCurrentTabMaskColor(int rgb) { 
204         mCurrentTabMaskColor = rgb; 
205     } 
206   
207     /** 
208      * 设置Tab选中后的颜色。为什么要重载这个方法呢?因为我总是记不住Alpha值0和255谁是全透明, 于是宁愿把ARGB颜色中A跟RGB分开设置…… 
209      *  
210      * @param rgb 
211      *            rgb颜色值 
212      * @param a 
213      *            alpha值 
214      */ 
215     public void setCurrentTabMaskColor(int rgb, int a) { 
216         mCurrentTabMaskColor = rgb; 
217         mCurrentTabMaskAlpha = a; 
218     } 
219   
220     /** 
221      * 获取Tab个数 
222      *  
223      * @return Tab个数 
224      */ 
225     public int getTabCount() { 
226         return mTabCount; 
227     } 
228   
229     @Override 
230     protected void onDraw(Canvas canvas) { 
231         super.onDraw(canvas); 
232         int h = this.getHeight(); 
233         if (mCurrentTabIndex > -1 && mCurrentTabIndex < mTabCount) { 
234             int startX = (mTabWidth + mSeperatorWidth) * mCurrentTabIndex; 
235             mPaint.setColor(mCurrentTabMaskColor); 
236             mPaint.setAlpha(mCurrentTabMaskAlpha); 
237             mPaint.setStyle(Paint.Style.FILL); 
238             canvas.drawRect(new Rect(startX, 0, startX + mTabWidth, h), mPaint); 
239         } 
240     } 
241   
242     /** 
243      * 从布局文件的属性值中解析出各个资源 
244      */ 
245     private void getResourcesFromXml() { 
246         TypedArray ta = mContext.obtainStyledAttributes(mAttrs, 
247                 R.styleable.TabBar, 0, 0); 
248         mIconNames = ta.getTextArray(R.styleable.TabBar_icons); 
249         mTitles = ta.getTextArray(R.styleable.TabBar_titles); 
250         mIconAboveTitle = ta 
251                 .getBoolean(R.styleable.TabBar_IconAboveTitle, true); 
252         mSeperator = ta.getDrawable(R.styleable.TabBar_Seperator); 
253         mSeperatorResId = ta.getResourceId(R.styleable.TabBar_Seperator, -1); 
254   
255         if (!StringHelper.notNullAndNotEmpty(mResPackageName)) 
256             mResPackageName = mContext.getPackageName(); 
257   
258         if (mTitles == null) { 
259             mTitles = new CharSequence[0]; // 避免为null 
260         } 
261   
262         if (mIconNames == null) { 
263             mIconNames = new CharSequence[0]; // 避免为null 
264         } 
265   
266         Resources res = mContext.getResources(); 
267         mIcons = new Drawable[mIconNames.length]; 
268         for (int i = 0; i < mIconNames.length; ++i) { 
269             int id = res.getIdentifier(mIconNames[i].toString(), “drawable”, 
270                     mResPackageName); 
271             if (id != 0) 
272                 mIcons[i] = res.getDrawable(id); 
273         } 
274   
275         ta.recycle(); 
276     } 
277   
278     public interface OnCurrentTabChangedListener { 
279         public void onCurrentTabChanged(int index); 
280     } 
281 } 


3. [文件]     arrays.xml ~ 304B    

 

01 <?xml version=“1.0” encoding=“utf-8”?> 
02 <resources> 
03   
04     <string-array name=“tab_titles”> 
05         <item>Tab1</item> 
06         <item>Tab2</item> 
07     </string-array> 
08     <string-array name=“tab_icons”> 
09         <item>tab1_icon</item> 
10         <item>tab2_icon</item> 
11     </string-array> 
12   
13 </resources> 

4. [文件]     activity_main.xml ~ 1002B

01 <RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android” 
02     xmlns:tools=“http://schemas.android.com/tools” 
03     xmlns:pupa=“http://schemas.android.com/apk/res/com.pupa.TabDemo” 
04     android:layout_width=“match_parent” 
05     android:layout_height=“match_parent” 
06     tools:context=“.MainActivity” > 
07   
08     <com.pupa.common.widget.TabBar 
09         android:id=“@+id/MainTabBar” 
10         android:layout_width=“match_parent” 
11         android:layout_height=“wrap_content” 
12         android:layout_alignParentTop=“true” 
13         android:background=“@android:drawable/title_bar” 
14         pupa:Seperator=“@drawable/tab_seperator” 
15         pupa:icons=“@array/tab_icons” 
16         pupa:titles=“@array/tab_titles” > 
17     </com.pupa.common.widget.TabBar> 
18   
19     <android.support.v4.view.ViewPager 
20         android:id=“@+id/MainViewPager” 
21         android:layout_width=“match_parent” 
22         android:layout_height=“match_parent” 
23         android:layout_below=“@id/MainTabBar” /> 
24   
25 </RelativeLayout> 

 

5. [文件]     TabBarDemo.zip ~ 703KB    

    eoe成立于2009年,创办了最早也是目前为止最大的中文移动开发者社区:www.eoe.cn 
超过160万的移动开发者汇聚在eoe,共同讨论最前沿最精彩的技术话题。eoe一直致力于为移动开发者提供全方位的服务。
    更多内容请关注eoe移动开发者社区:
http://www.eoeandroid.com/

这篇关于【Android源代码】Android自定义的TabBar的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1034943

相关文章

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

C语言自定义类型之联合和枚举解读

《C语言自定义类型之联合和枚举解读》联合体共享内存,大小由最大成员决定,遵循对齐规则;枚举类型列举可能值,提升可读性和类型安全性,两者在C语言中用于优化内存和程序效率... 目录一、联合体1.1 联合体类型的声明1.2 联合体的特点1.2.1 特点11.2.2 特点21.2.3 特点31.3 联合体的大小1

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期