├── .gitignore
├── README-2.0.md
├── README-en-2.0.md
├── README-en.md
├── README.md
├── apk
└── demo.apk
├── build.gradle
├── demo
├── .gitignore
├── build.gradle
├── demo.jks
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── chaychan
│ │ └── bottombarlayout
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── cart.json
│ │ ├── category.json
│ │ ├── home.json
│ │ └── mine.json
│ ├── java
│ │ └── com
│ │ │ └── chaychan
│ │ │ └── bottombarlayout
│ │ │ ├── BaseViewPagerActivity.java
│ │ │ ├── DemoBean.java
│ │ │ ├── DynamicAddItemActivity.java
│ │ │ ├── FragmentManagerActivity.java
│ │ │ ├── LottieDemoActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── TabFragment.java
│ │ │ ├── ViewPager2DemoActivity.java
│ │ │ └── ViewPagerDemoActivity.java
│ └── res
│ │ ├── drawable
│ │ └── selector_bg.xml
│ │ ├── layout
│ │ ├── activity_dynamic_add_item.xml
│ │ ├── activity_fragment_manager.xml
│ │ ├── activity_lottie_demo.xml
│ │ ├── activity_main.xml
│ │ ├── activity_view_pager2_demo.xml
│ │ └── activity_view_pager_demo.xml
│ │ ├── menu
│ │ ├── menu_demo.xml
│ │ └── menu_dynamic.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ ├── icon_add.png
│ │ ├── tab_home_normal.png
│ │ ├── tab_home_selected.png
│ │ ├── tab_loading.png
│ │ ├── tab_me_normal.png
│ │ ├── tab_me_selected.png
│ │ ├── tab_micro_normal.png
│ │ ├── tab_micro_selected.png
│ │ ├── tab_video_normal.png
│ │ └── tab_video_selected.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── chaychan
│ └── bottombarlayout
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── intro_img
├── 4.png
├── display1.gif
├── display2.gif
├── display3.gif
├── download_qr.png
├── float.gif
├── lottie.gif
└── transfer_code.jpg
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── chaychan
│ │ └── library
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── chaychan
│ │ │ └── library
│ │ │ ├── BottomBarItem.java
│ │ │ ├── BottomBarLayout.java
│ │ │ ├── MyLottieAnimationView.java
│ │ │ ├── TabData.java
│ │ │ └── UIUtils.java
│ └── res
│ │ ├── drawable
│ │ ├── shape_msg.xml
│ │ ├── shape_notify_point.xml
│ │ └── shape_unread.xml
│ │ ├── layout
│ │ └── item_bottom_bar.xml
│ │ └── values
│ │ ├── attr.xml
│ │ ├── colors.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── chaychan
│ └── library
│ └── ExampleUnitTest.java
├── settings.gradle
├── update-note-en.md
└── update-note.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | /.idea
11 |
--------------------------------------------------------------------------------
/README-2.0.md:
--------------------------------------------------------------------------------
1 | [英文(English)](https://github.com/chaychan/BottomBarLayout/blob/master/README-en-2.0.md)
2 |
3 | ### 支持
4 |
5 | 如果觉得我的项目对你有所帮助的话,帮我点下**Star** 吧,让更多人的人可以看到,谢谢!
6 |
7 | ### 轻量级的底部导航栏
8 | 目前市场上的App,几乎都有底部页签导航栏,所以我们在开发的时候经常需要用到这个,虽然github上有不少已经封装好的底部导航栏的工具,例如bottombar,alphaIndicator(仿微信滑动渐变底部控件)等,但是这些控件由于功能太多,而且也没有给予详细的介绍文档,所以用起来不是特别容易,有时候我们仅仅只是想要一个简简单单的底部导航,但是又不想去自己在布局中搞一个个LinearLayout或者RadioGroup,然后切换页签的时候更换图标,让ViewPager跳转到对应的页面等一系列繁琐的操作,这时候,你可以使用BottomBarLayout,简简单单就可以实现以下效果:
9 |
10 | ### 我的博客
11 |
12 | [http://chaychan.tech](http://chaychan.tech)
13 |
14 | #### 下载体验
15 |
16 | [点击下载体验](https://raw.githubusercontent.com/chaychan/BottomBarLayout/master/apk/demo.apk)
17 |
18 | 扫码下载:
19 |
20 | 
21 |
22 |
23 | #### **导入方式**
24 |
25 | 在项目根目录下的build.gradle中的allprojects{}中,添加jitpack仓库地址,如下:
26 |
27 | allprojects {
28 | repositories {
29 | jcenter()
30 | maven { url 'https://jitpack.io' }//添加jitpack仓库地址
31 | }
32 | }
33 |
34 | 打开app的module中的build.gradle,在dependencies{}中,添加依赖,如下:
35 |
36 | dependencies {
37 | implementation 'com.github.chaychan:BottomBarLayout:2.1.0' //建议使用最新版本
38 | }
39 |
40 |
41 | 最新发布的版本可以查看
42 |
43 | [https://github.com/chaychan/BottomBarLayout/releases](https://github.com/chaychan/BottomBarLayout/releases)
44 |
45 | 
46 |
47 | #### 显示未读数、提示小红点、提示消息
48 |
49 | 
50 |
51 | #### 支持lottie
52 |
53 | 
54 |
55 | #### 历史版本更新说明
56 |
57 | [历史更新记录](https://github.com/chaychan/BottomBarLayout/blob/master/update-note.md)
58 |
59 | ### BottomBarLayout的使用
60 |
61 | #### BottomBarItem属性介绍
62 | ```
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | ```
113 | #### 布局文件中配置
114 |
115 | 在xml文件中,配置BottomBarLayout,包裹子条目BottomBarItem
116 | ```
117 |
118 |
125 |
126 |
132 |
133 |
142 |
143 |
155 |
156 |
168 |
169 |
170 |
182 |
183 |
195 |
196 |
197 |
198 |
199 | ```
200 |
201 | #### java文件中设置
202 |
203 | 找过对应的ViewPager和BottomBarLayout,为ViewPager设置Adapter,然后为BottomBarLayout设置ViewPager
204 | ```
205 | mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
206 | mBottomBarLayout.setViewPager(mVpContent);
207 | ```
208 | 这样就实现底部导航栏功能了
209 |
210 | #### 动态添加条目
211 | ```
212 | for (int i = 0; i < mTitleIds.length; i++) {
213 | //创建item
214 | BottomBarItem item = createBottomBarItem(i);
215 | mBottomBarLayout.addItem(item); //添加条目
216 |
217 | TabFragment homeFragment = createFragment(mTitleIds[i]);
218 | mFragmentList.add(homeFragment);
219 | }
220 |
221 | private BottomBarItem createBottomBarItem(int i) {
222 | BottomBarItem item = new BottomBarItem.Builder(this)
223 | .titleTextSize(8)
224 | .titleNormalColor(R.color.tab_normal_color)
225 | .titleSelectedColor(R.color.tab_selected_color)
226 | // .openTouchBg(false)
227 | // .marginTop(5)
228 | // .itemPadding(5)
229 | // .unreadNumThreshold(99)
230 | // .unreadTextColor(R.color.white)
231 |
232 | //还有很多属性,详情请查看Builder里面的方法
233 | .create(mNormalIconIds[i], mSelectedIconIds[i], getString(mTitleIds[i]));
234 | return item;
235 | }
236 | ```
237 | #### 移除条目
238 | ```
239 | mBottomBarLayout.removeItem(0);
240 | ```
241 | #### 开启滑动效果
242 |
243 | 页签之间的切换默认关闭了滑动效果,如果需要开启可以通过调用BottomBarLayout的setSmoothScroll()方法:
244 | ```
245 | mBottomBarLayout.setSmoothScroll(true);
246 | ```
247 | 也可以在布局文件中指定BottomBarLayout的smoothScroll属性为true
248 |
249 | 开启后效果如下:
250 |
251 | 
252 |
253 | #### 跳转前拦截
254 | ```
255 | mBottomBarLayout.setOnPageChangeInterceptor(position -> {
256 | boolean isLogin = false; //模拟没有登录
257 | if (position == TAB_POSITION_ME && !isLogin){
258 | //no login intercept to other tab or to LoginActivity
259 | Toast.makeText(ViewPager2DemoActivity.this, "Test intercept, Login first please", Toast.LENGTH_SHORT).show();
260 | return true;
261 | }
262 | return false;
263 | });
264 | ```
265 |
266 | #### 设置条目选中的监听
267 | ```
268 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
269 | //do something
270 | });
271 | ```
272 |
273 | #### 设置同个tab重复点击是否回调setOnItemSelectedListener
274 | ```
275 | app:sameTabClickCallBack="true" //默认为false
276 | ```
277 |
278 | #### 显示未读数、提示小红点、提示消息
279 | ```
280 | mBottomBarLayout.setUnread(0,20);//设置第一个页签的未读数为20
281 | mBottomBarLayout.setUnread(1,101);//设置第二个页签的未读数
282 | mBottomBarLayout.showNotify(2);//设置第三个页签显示提示的小红点
283 | mBottomBarLayout.setMsg(3,"NEW");//设置第四个页签显示NEW提示文字
284 | ```
285 | 当设置的未读数小于或等于0时,消失未读数的小红点将会消失;
286 | 当未读数为1-99时,则显示对应的数字;
287 | 当未读数大于99时,显示99+;
288 |
289 | #### 设置未读数阈值
290 | 未读数的阈值可以指定BottomBarItem的unreadThreshold属性设置,默认该值为99,如设置 app:unreadThreshold="999" , 若未读数超过该值,则显示"999+"。
291 |
292 | #### 隐藏提示小红点、提示消息
293 | ```
294 | mBottomBarLayout.hideNotify(2);//隐藏第三个页签显示提示的小红点
295 | mBottomBarLayout.hideMsg(3);//隐藏第四个页签显示的提示文字
296 | ```
297 |
298 | #### 设置未读数字体颜色
299 | ```
300 | app:unreadTextColor="@color/unreadTextColor"
301 | ```
302 | #### 设置未读数背景
303 | ```
304 | app:unreadTextBg="@drawable/shape_unread"
305 | ```
306 | drawable的编写如下:
307 | ```
308 |
309 |
310 |
311 |
312 |
313 |
314 | ```
315 | #### 设置提示文字字体颜色、背景
316 | ```
317 | app:msgTextColor="@color/msgTextColor"
318 | app:msgTextBg="@drawable/shape_msg"
319 | ```
320 | #### 设置提示点背景
321 | ```
322 | app:notifyPointBg="@drawable/shape_notify_point"
323 | ```
324 | #### 设置lottie文件名
325 | ```
326 | app:lottieJson="home.json"
327 | ```
328 | home.json存放在assets目录中,如果要设置lottie的宽高还是使用iconWidth、iconHeight属性
329 |
330 | #### BottomBarItem的介绍
331 | BottomBarItem继承于LinearLayout,其子View有显示图标的ImageView和展示文字的TextView,分别可以通过getImageView()和getTextView()方法获取到对应的子控件。github上不少底部导航栏的控件都没能获取到对应的子控件,所以在需要对子控件进行操作的时候极不方便,有一些的思路并不是用ImageView和TextView,而是用绘制的,所以也不能获取到对应的显示图标的控件或展示文字的控件,造成无法获取到该控件,无法进行一些业务上的操作,比如类似今日头条的底部的首页,点击首页的页签,会更换成加载中的图标,执行旋转动画,BottomBarLayout可以轻松地做到这个需求。
332 |
333 | 演示效果如下:
334 |
335 | 
336 |
337 |
338 | 只需为BottomBarLayout设置页签选中的监听,在回调中进行以下处理:
339 | ```
340 | mBottomBarLayout.setOnItemSelectedListener(new BottomBarLayout.OnItemSelectedListener() {
341 | @Override
342 | public void onItemSelected(final BottomBarItem bottomBarItem, int previousPosition, final int currentPosition) {
343 | Log.i("MainActivity", "position: " + currentPosition);
344 | if (currentPosition == 0) {
345 | //如果是第一个,即首页
346 | if (previousPosition == currentPosition) {
347 | //如果是在原来位置上点击,更换首页图标并播放旋转动画
348 | if (mRotateAnimation != null && !mRotateAnimation.hasEnded()){
349 | //如果当前动画正在执行
350 | return;
351 | }
352 |
353 | bottomBarItem.setSelectedIcon(R.mipmap.tab_loading);//更换成加载图标
354 |
355 | //播放旋转动画
356 | if (mRotateAnimation == null) {
357 | mRotateAnimation = new RotateAnimation(0, 360,
358 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
359 | 0.5f);
360 | mRotateAnimation.setDuration(800);
361 | mRotateAnimation.setRepeatCount(-1);
362 | }
363 | ImageView bottomImageView = bottomBarItem.getImageView();
364 | bottomImageView.setAnimation(mRotateAnimation);
365 | bottomImageView.startAnimation(mRotateAnimation);//播放旋转动画
366 |
367 | //模拟数据刷新完毕
368 | mHandler.postDelayed(new Runnable() {
369 | @Override
370 | public void run() {
371 | boolean tabNotChanged = mBottomBarLayout.getCurrentItem() == currentPosition; //是否还停留在当前页签
372 | bottomBarItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换成首页原来选中图标
373 | cancelTabLoading(bottomBarItem);
374 | }
375 | }, 3000);
376 | return;
377 | }
378 | }
379 |
380 | //如果点击了其他条目
381 | BottomBarItem bottomItem = mBottomBarLayout.getBottomItem(0);
382 | bottomItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换为原来的图标
383 |
384 | cancelTabLoading(bottomItem);//停止旋转动画
385 | }
386 | });
387 |
388 |
389 | /**停止首页页签的旋转动画*/
390 | private void cancelTabLoading(BottomBarItem bottomItem) {
391 | Animation animation = bottomItem.getImageView().getAnimation();
392 | if (animation != null){
393 | animation.cancel();
394 | }
395 | }
396 | ```
397 | #### 实现思路:
398 |
399 | 1.当点击页签加载的时候,BottomBarItem通过调用setIconSelectedResourceId()设置成选中状态下的图标资源id为加载中图标的资源id,完成图标的更换操作;
400 |
401 | 2.通过BottomBarItem获取到对应页签的ImageView,对其设置旋转动画,执行旋转动画,当点击其他页签或者数据加载完成后,更换回原来的选中图标,停止旋转动画。
402 |
403 | 好了,到这里BottomBarLayout的介绍就到此为止了,之所以封装这个控件主要是为了方便开发,希望可以帮助到更多人,如果大家有什么想法或者意见不妨向我提出,我会不断完善BottomBarLayout的。
404 |
405 |
406 | #### 支持和鼓励
407 |
408 | 如果觉得我的项目对你有所帮助的话,不妨打赏一下吧!这样我会更加有动力去完善好这个项目:
409 |
410 | 微信赞赏:
411 |
412 | 
413 |
414 |
415 |
--------------------------------------------------------------------------------
/README-en-2.0.md:
--------------------------------------------------------------------------------
1 | [中文(Chinese)](https://github.com/chaychan/BottomBarLayout)
2 |
3 | ### Support
4 |
5 | If you feel that my project is helpful to you, please help me to click on the **star** and let more people see it. Thank you!
6 |
7 | ### Introduction
8 | Currently, App on the market almost has a navigation bar at the bottom, so we often need to use this during development. Although there are many tools on the github packaged bottom navigation bar, such as bottombar, alphaIndicator Swipe gradient bottom controls etc., but these controls are not particularly easy to use due to too many functions and no detailed documentation. Sometimes we just want a simple bottom navigation, but we don't want to go One by one in the layout of the LinearLayout or RadioGroup, and then change the tab icon, let ViewPager jump to the corresponding page and a series of tedious operations, this time, you can use BottomBarLayout, simply can achieve the following effect:
9 |
10 | #### Apk
11 |
12 | [click to download](https://raw.githubusercontent.com/chaychan/BottomBarLayout/master/apk/demo.apk)
13 |
14 | or scan the QR code
15 |
16 | 
17 |
18 |
19 | #### **How to import**
20 |
21 | Add the jitpack repository address in allprojects{} in build.gradle in the project root directory, as follows:
22 | ```
23 | allprojects {
24 | repositories {
25 | jcenter()
26 | maven { url 'https://jitpack.io' }//Add jitpack warehouse address
27 | }
28 | }
29 | ```
30 | Open the build.gradle in the app's module, add dependencies in dependencies {} as follows:
31 | ```
32 | dependencies {
33 | compile 'com.github.chaychan:BottomBarLayout:2.1.0' //It is recommended to use the latest version
34 | }
35 | ```
36 |
37 | The latest version can be viewed
38 |
39 | [https://github.com/chaychan/BottomBarLayout/releases](https://github.com/chaychan/BottomBarLayout/releases)
40 |
41 | ### Demo
42 |
43 | 
44 |
45 | #### Display unread, show red dot, display message
46 |
47 | 
48 |
49 | #### Support to use lottie
50 |
51 | 
52 |
53 | #### Historical version update notes
54 |
55 | [Historical version update notes](https://github.com/chaychan/BottomBarLayout/blob/master/update-note-en.md)
56 |
57 | ### Usage
58 |
59 | #### Attribute introduction
60 | ```
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | ```
117 | #### Configuration in the layout file
118 |
119 | In the xml file, configure BottomBarLayout to wrap the sub-item BottomBarItem
120 | ```
121 |
122 |
129 |
130 |
136 |
137 |
146 |
147 |
159 |
160 |
172 |
173 |
174 |
186 |
187 |
199 |
200 |
201 |
202 |
203 |
204 | ```
205 | #### Java file settings
206 |
207 | Find the corresponding ViewPager and BottomBarLayout, set Adapter for ViewPager, and then set the ViewPager for BottomBarLayout
208 | ```
209 | mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
210 | mBottomBarLayout.setViewPager(mVpContent);
211 | ```
212 | This realizes the bottom navigation bar function
213 |
214 | #### Add item dynamically
215 | ```
216 | for (int i = 0; i < mTitleIds.length; i++) {
217 | //create item
218 | BottomBarItem item = createBottomBarItem(i);
219 | mBottomBarLayout.addItem(item); //addItem
220 |
221 | TabFragment homeFragment = createFragment(mTitleIds[i]);
222 | mFragmentList.add(homeFragment);
223 | }
224 |
225 |
226 | private BottomBarItem createBottomBarItem(int i) {
227 | BottomBarItem item = new BottomBarItem.Builder(this)
228 | .titleTextSize(8)
229 | .titleNormalColor(R.color.tab_normal_color)
230 | .titleSelectedColor(R.color.tab_selected_color)
231 | // .openTouchBg(false)
232 | // .marginTop(5)
233 | // .itemPadding(5)
234 | // .unreadNumThreshold(99)
235 | // .unreadTextColor(R.color.white)
236 |
237 | //There are still many properties, please see the methods in the Builder for details.
238 | .create(mNormalIconIds[i], mSelectedIconIds[i], getString(mTitleIds[i]));
239 | return item;
240 | }
241 | ```
242 | #### Remove item
243 | ```
244 | mBottomBarLayout.removeItem(0);
245 | ```
246 | #### Turn on the slide effect
247 |
248 | Tab switch between the closure of the default sliding effect, if you need to open the setSmoothScroll () method can be called by calling BottomBarLayout:
249 | ```
250 | mBottomBarLayout.setSmoothScroll(true);
251 | ```
252 | You can also specify BottomBarLayout's smoothScroll property to be true in the layout file.
253 |
254 | The effect after opening is as follows:
255 |
256 | 
257 |
258 | #### Intercept before jump
259 | ```
260 | mBottomBarLayout.setOnPageChangeInterceptor(position -> {
261 | boolean isLogin = false; //Simulate no login
262 | if (position == TAB_POSITION_ME && !isLogin){
263 | //no login intercept to other tab or to LoginActivity
264 | Toast.makeText(ViewPager2DemoActivity.this, "Test intercept, Login first please", Toast.LENGTH_SHORT).show();
265 | return true;
266 | }
267 | return false;
268 | });
269 | ```
270 |
271 | #### Set the item selected listener
272 | ```
273 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
274 | //do something
275 | });
276 | ```
277 | #### Display unread, show red dot, display message
278 | ```
279 | mBottomBarLayout.setUnread(0,20);//Set the first tab's unread 20
280 | mBottomBarLayout.setUnread(1,101);//Set the first tab's unread 101
281 | mBottomBarLayout.showNotify(2);//The third page shows the tips of the little red dot
282 | mBottomBarLayout.setMsg(3,"NEW");//The fourth tab shows the NEW text
283 | ```
284 | When the setting of unread less than or equal to 0, the disappearance of small red dot will disappear;
285 | When the unread count is 1-99, the corresponding number is displayed.
286 | When unread more than 99, it shows 99+;
287 |
288 | #### Set unaware reading threshold
289 | The unread threshold may specify the BottomBarItem's unreadThreshold property setting, which defaults to 99. For example, if app:unreadThreshold="999" is set, if the reading does not exceed this value, "999+" is displayed。
290 |
291 | #### Hidden tips red dot, tips message
292 | ```
293 | mBottomBarLayout.hideNotify(2);//Hide the third page shows the tips of the little red dot
294 | mBottomBarLayout.hideMsg(3);//Hide the text displayed on the fourth tab
295 | ```
296 | #### Set unread font color
297 | ```
298 | app:unreadTextColor="@color/unreadTextColor"
299 | ```
300 | #### Set the unread background
301 | ```
302 | app:unreadTextBg="@drawable/shape_unread"
303 | ```
304 | Drawable is written as follows:
305 | ```
306 |
307 |
308 |
309 |
310 |
311 |
312 | ```
313 | #### Set prompt text font color, background
314 | ```
315 | app:msgTextColor="@color/msgTextColor"
316 | app:msgTextBg="@drawable/shape_msg"
317 | ```
318 | #### Set prompt point background
319 | ```
320 | app:notifyPointBg="@drawable/shape_notify_point"
321 | ```
322 | #### Set the name of lottie file
323 | ```
324 | app:lottieJson="home.json"
325 | ```
326 | "home.json" is stored in the assets directory. To set Lottie's width and height, use the iconWidth and iconHeight attributes
327 |
328 |
329 | Well, here's the introduction of BottomBarLayout stop here, the reason for the package this control is mainly for the convenience of development, hope to help more people, if you have any ideas or comments may wish to put forward to me, I will continue to improve BottomBarLayout of.
330 |
331 |
332 | #### Support and encouragement
333 |
334 | If you think my project is helpful to you, star! So I will be more motivated to improve this project.
335 |
336 |
337 |
--------------------------------------------------------------------------------
/README-en.md:
--------------------------------------------------------------------------------
1 | [中文(Chinese)](https://github.com/chaychan/BottomBarLayout)
2 |
3 | ### Support
4 |
5 | If you feel that my project is helpful to you, please help me to click on the **star** and let more people see it. Thank you!
6 |
7 | ### Version 3.0
8 |
9 | The new version 3.0 has refactored the project, and the usage is very different. If you used version 2.0 before, please refer to [BottomBarLayout-2.1.0](https://github.com/chaychan/BottomBarLayout/blob/master/README-en-2.0.md) for detailed usage if you want to continue to update and maintain it. The old version will no longer be maintained. If necessary, please download the source code and modify it. The branch name is feature-2.1.0
10 |
11 | ### Introduction
12 | Currently, App on the market almost has a navigation bar at the bottom, so we often need to use this during development. Although there are many tools on the github packaged bottom navigation bar, such as bottombar, alphaIndicator Swipe gradient bottom controls etc., but these controls are not particularly easy to use due to too many functions and no detailed documentation. Sometimes we just want a simple bottom navigation, but we don't want to go One by one in the layout of the LinearLayout or RadioGroup, and then change the tab icon, let ViewPager jump to the corresponding page and a series of tedious operations, this time, you can use BottomBarLayout, simply can achieve the following effect:
13 |
14 | #### Apk
15 |
16 | [click to download](https://raw.githubusercontent.com/chaychan/BottomBarLayout/master/apk/demo.apk)
17 |
18 | or scan the QR code
19 |
20 | 
21 |
22 |
23 | #### **How to import**
24 |
25 | Add the jitpack repository address in allprojects{} in build.gradle in the project root directory, as follows:
26 | ```
27 | allprojects {
28 | repositories {
29 | jcenter()
30 | maven { url 'https://jitpack.io' }//Add jitpack
31 | }
32 | }
33 | ```
34 | Open the build.gradle in the app's module, add dependencies in dependencies {} as follows:
35 | ```
36 | dependencies {
37 | implementation 'com.github.chaychan:BottomBarLayout:3.0.0' //It is recommended to use the latest version
38 | }
39 | ```
40 |
41 | The latest version can be viewed
42 |
43 | [https://github.com/chaychan/BottomBarLayout/releases](https://github.com/chaychan/BottomBarLayout/releases)
44 |
45 | ### Demo
46 |
47 | #### Supports raising the middle icon and intercepting before clicking
48 | 
49 |
50 | #### Display unread, show red dot, display message
51 |
52 | 
53 |
54 | #### Support to use lottie
55 |
56 | 
57 |
58 | #### Historical version update notes
59 |
60 | [Historical version update notes](https://github.com/chaychan/BottomBarLayout/blob/master/update-note-en.md)
61 |
62 | ### Usage
63 |
64 | #### Attribute introduction
65 | ```
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | ```
128 |
129 | #### Configuration in the layout file
130 |
131 | In the xml file, configure BottomBarLayout to wrap the sub-item BottomBarItem
132 |
133 | ```
134 |
135 |
141 |
142 |
147 |
148 |
162 |
163 |
164 | ```
165 |
166 | #### Java file settings
167 |
168 | Find the corresponding ViewPager2 and BottomBarLayout, set Adapter for ViewPager2, and then set the ViewPager2 for BottomBarLayout
169 | ```
170 | protected List getTabData() {
171 | List tabData = new ArrayList<>();
172 | tabData.add(new TabData("首页", R.mipmap.tab_home_normal, R.mipmap.tab_home_selected));
173 | tabData.add(new TabData("视频", R.mipmap.tab_video_normal, R.mipmap.tab_video_selected));
174 | tabData.add(new TabData("微头条", R.mipmap.tab_micro_normal, R.mipmap.tab_micro_selected));
175 | tabData.add(new TabData("我的", R.mipmap.tab_me_normal, R.mipmap.tab_me_selected));
176 |
177 | //If it is lottie. The lottie file storage location: /src/main/assets
178 | //tabData.add(new TabData("首页", "home.json"));
179 | //tabData.add(new TabData("分类", "category.json"));
180 | //tabData.add(new TabData("购物车", "cart.json"));
181 | //tabData.add(new TabData("我的", "mine.json"));
182 |
183 | return tabData;
184 | }
185 |
186 | ...
187 | mBottomBarLayout.setData(tabData); //Setting up the data source
188 | mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
189 | mBottomBarLayout.setViewPager2(mVpContent);
190 | ```
191 |
192 | This realizes the bottom navigation bar function
193 |
194 | #### Set the middle icon to be raised
195 | ```
196 |
197 |
202 |
203 |
208 |
209 |
236 |
237 |
238 | ```
239 |
240 |
241 | #### Add item dynamically
242 |
243 | ```
244 | for (int i = 0; i < mTitleIds.length; i++) {
245 | //create item
246 | BottomBarItem item = createBottomBarItem(i);
247 | mBottomBarLayout.addItem(item); //addItem
248 |
249 | TabFragment homeFragment = createFragment(mTitleIds[i]);
250 | mFragmentList.add(homeFragment);
251 | }
252 |
253 |
254 | private BottomBarItem createBottomBarItem(int i) {
255 | BottomBarItem item = new BottomBarItem.Builder(this)
256 | .titleTextSize(8)
257 | .titleNormalColor(R.color.tab_normal_color)
258 | .titleSelectedColor(R.color.tab_selected_color)
259 | // .marginTop(5)
260 | // .itemPadding(5)
261 | // .unreadNumThreshold(99)
262 | // .unreadTextColor(R.color.white)
263 |
264 | //There are still many properties, please see the methods in the Builder for details.
265 | .create(mNormalIconIds[i], mSelectedIconIds[i], getString(mTitleIds[i]));
266 | return item;
267 | }
268 | ```
269 |
270 | #### Remove item
271 | ```
272 | mBottomBarLayout.removeItem(0);
273 | ```
274 | #### Turn on the slide effect
275 |
276 | Tab switch between the closure of the default sliding effect, if you need to open the setSmoothScroll () method can be called by calling BottomBarLayout:
277 | ```
278 | mBottomBarLayout.setSmoothScroll(true);
279 | ```
280 | You can also specify BottomBarLayout's smoothScroll property to be true in the layout file.
281 |
282 | The effect after opening is as follows:
283 |
284 | 
285 |
286 | #### Intercept before jump
287 | ```
288 | mBottomBarLayout.setOnPageChangeInterceptor(position -> {
289 | boolean isLogin = false; //Simulate no login
290 | if (position == TAB_POSITION_ME && !isLogin){
291 | //no login intercept to other tab or to LoginActivity
292 | Toast.makeText(ViewPager2DemoActivity.this, "Test intercept, Login first please", Toast.LENGTH_SHORT).show();
293 | return true;
294 | }
295 | return false;
296 | });
297 | ```
298 |
299 | #### Set the item selected listener
300 | ```
301 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
302 | //do something
303 | });
304 | ```
305 |
306 | #### Set whether to call back setOnItemSelectedListener when clicking the same tab repeatedly
307 | ```
308 | app:sameTabClickCallBack="true" //Defaults to false
309 | ```
310 |
311 | #### Display unread, show red dot, display message
312 | ```
313 | mBottomBarLayout.setUnread(0,20);//Set the first tab's unread 20
314 | mBottomBarLayout.setUnread(1,101);//Set the first tab's unread 101
315 | mBottomBarLayout.showNotify(2);//The third page shows the tips of the little red dot
316 | mBottomBarLayout.setMsg(3,"NEW");//The fourth tab shows the NEW text
317 | ```
318 | When the setting of unread less than or equal to 0, the disappearance of small red dot will disappear;
319 | When the unread count is 1-99, the corresponding number is displayed.
320 | When unread more than 99, it shows 99+;
321 |
322 | #### Set unaware reading threshold
323 | The unread threshold may specify the BottomBarItem's unreadThreshold property setting, which defaults to 99. For example, if app:unreadThreshold="999" is set, if the reading does not exceed this value, "999+" is displayed。
324 |
325 | #### Hidden tips red dot, tips message
326 | ```
327 | mBottomBarLayout.hideNotify(2);//Hide the third page shows the tips of the little red dot
328 | mBottomBarLayout.hideMsg(3);//Hide the text displayed on the fourth tab
329 | ```
330 | #### Set unread font color
331 | ```
332 | app:unreadTextColor="@color/unreadTextColor"
333 | ```
334 | #### Set the unread background
335 | ```
336 | app:unreadTextBg="@drawable/shape_unread"
337 | ```
338 | Drawable is written as follows:
339 | ```
340 |
341 |
342 |
343 |
344 |
345 |
346 | ```
347 | #### Set prompt text font color, background
348 | ```
349 | app:msgTextColor="@color/msgTextColor"
350 | app:msgTextBg="@drawable/shape_msg"
351 | ```
352 | #### Set prompt point background
353 | ```
354 | app:notifyPointBg="@drawable/shape_notify_point"
355 | ```
356 |
357 |
358 | Well, here's the introduction of BottomBarLayout stop here, the reason for the package this control is mainly for the convenience of development, hope to help more people, if you have any ideas or comments may wish to put forward to me, I will continue to improve BottomBarLayout of.
359 |
360 |
361 | #### Support and encouragement
362 |
363 | If you think my project is helpful to you, star! So I will be more motivated to improve this project.
364 |
365 |
366 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [英文(English)](https://github.com/chaychan/BottomBarLayout/blob/master/README-en.md)
2 |
3 | ### 支持
4 |
5 | 如果觉得我的项目对你有所帮助的话,帮我点下**Star** 吧,让更多人的人可以看到,谢谢!
6 |
7 | ### 旧版本
8 |
9 | 新版本3.0对项目进行了重构,用法有很大不同,如果你之前使用的是2.0版本,想继续更新维护详细用法请参考
10 | [BottomBarLayout-2.1.0](https://github.com/chaychan/BottomBarLayout/blob/master/README-2.0.md), 旧版本的实现后续将不再维护,若有需要请下载源码修改,分支名feature-2.1.0
11 |
12 | ### 2024-07-17
13 |
14 | 距离上次更新接近4年,看到依旧有不少小伙伴提issue,很抱歉没有及时回复,趁着这段时间空闲,针对issue进行了修复,如:
15 |
16 | - [#57](https://github.com/chaychan/BottomBarLayout/issues/57) —— 支持ViewPager2
17 | - [#56](https://github.com/chaychan/BottomBarLayout/issues/56) —— 针对某些场景添加跳转前拦截
18 |
19 | 以及一些小问题的修复,发布了2.1.0版本,如果项目之前集成了这个库,可以更新下这个版本
20 |
21 | 同时想着让大家可以更方便地使用这个库,对该库进行了重构,发布3.0版本,看有不少人提了中间凸起图标的需求,
22 | 特意支持中间图标凸起,详情请看文档介绍。
23 |
24 | ### 轻量级的底部导航栏
25 | 目前市场上的App,几乎都有底部页签导航栏,所以我们在开发的时候经常需要用到这个,虽然github上有不少已经封装好的底部导航栏的工具,例如bottombar,alphaIndicator(仿微信滑动渐变底部控件)等,但是这些控件由于功能太多,而且也没有给予详细的介绍文档,所以用起来不是特别容易,有时候我们仅仅只是想要一个简简单单的底部导航,但是又不想去自己在布局中搞一个个LinearLayout或者RadioGroup,然后切换页签的时候更换图标,让ViewPager跳转到对应的页面等一系列繁琐的操作,这时候,你可以使用BottomBarLayout,简简单单就可以实现以下效果:
26 |
27 | ### 我的博客
28 |
29 | [http://chaychan.tech](http://chaychan.tech)
30 |
31 |
32 | #### 下载体验
33 |
34 | [点击下载体验](https://raw.githubusercontent.com/chaychan/BottomBarLayout/master/apk/demo.apk)
35 |
36 | 扫码下载:
37 |
38 | 
39 |
40 |
41 | #### **导入方式**
42 |
43 | 在项目根目录下的build.gradle中的allprojects{}中,添加jitpack仓库地址,如下:
44 |
45 | ```
46 | allprojects {
47 | repositories {
48 | jcenter()
49 | maven { url 'https://jitpack.io' }//添加jitpack仓库地址
50 | }
51 | }
52 |
53 | ```
54 |
55 | 打开app的module中的build.gradle,在dependencies{}中,添加依赖,如下:
56 |
57 | ```
58 | dependencies {
59 | implementation 'com.github.chaychan:BottomBarLayout:3.0.0' //建议使用最新版本
60 | }
61 |
62 | ```
63 |
64 | 最新发布的版本可以查看
65 |
66 | [https://github.com/chaychan/BottomBarLayout/releases](https://github.com/chaychan/BottomBarLayout/releases)
67 |
68 | #### 支持中间图标凸起,点击跳转前拦截
69 | 
70 |
71 | #### 显示未读数、提示小红点、提示消息
72 |
73 | 
74 |
75 | #### 支持lottie
76 |
77 | 
78 |
79 | #### 历史版本更新说明
80 |
81 | [历史更新记录](https://github.com/chaychan/BottomBarLayout/blob/master/update-note.md)
82 |
83 | ### BottomBarLayout的使用
84 | ```
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | ```
147 | #### 布局文件中配置
148 |
149 | 在xml文件中,配置BottomBarLayout
150 |
151 | ```
152 |
153 |
159 |
160 |
165 |
166 |
180 |
181 |
182 | ```
183 |
184 |
185 | #### java文件中设置
186 |
187 | 找过对应的ViewPager和BottomBarLayout,为ViewPager设置Adapter,然后为BottomBarLayout设置ViewPager
188 |
189 | ```
190 | protected List getTabData() {
191 | List tabData = new ArrayList<>();
192 | tabData.add(new TabData("首页", R.mipmap.tab_home_normal, R.mipmap.tab_home_selected));
193 | tabData.add(new TabData("视频", R.mipmap.tab_video_normal, R.mipmap.tab_video_selected));
194 | tabData.add(new TabData("微头条", R.mipmap.tab_micro_normal, R.mipmap.tab_micro_selected));
195 | tabData.add(new TabData("我的", R.mipmap.tab_me_normal, R.mipmap.tab_me_selected));
196 |
197 | //如果是lottie lottie文件存放位置 /src/main/assets
198 | //tabData.add(new TabData("首页", "home.json"));
199 | //tabData.add(new TabData("分类", "category.json"));
200 | //tabData.add(new TabData("购物车", "cart.json"));
201 | //tabData.add(new TabData("我的", "mine.json"));
202 |
203 | return tabData;
204 | }
205 |
206 | ...
207 | mBottomBarLayout.setData(tabData); //设置数据源
208 | //和ViewPager2联动
209 | mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
210 | mBottomBarLayout.setViewPager2(mVpContent);
211 | ```
212 |
213 | 这样就实现底部导航栏功能了
214 |
215 | #### 设置中间图标凸起
216 | ```
217 |
218 |
223 |
224 |
229 |
230 |
257 |
258 |
259 | ```
260 |
261 | #### 动态添加条目
262 |
263 | ```
264 | for (int i = 0; i < mTitleIds.length; i++) {
265 | //创建item
266 | BottomBarItem item = createBottomBarItem(i);
267 | mBottomBarLayout.addItem(item); //添加条目
268 |
269 | TabFragment homeFragment = createFragment(mTitleIds[i]);
270 | mFragmentList.add(homeFragment);
271 | }
272 |
273 | private BottomBarItem createBottomBarItem(int i) {
274 | BottomBarItem item = new BottomBarItem.Builder(this)
275 | .titleTextSize(8)
276 | .titleNormalColor(R.color.tab_normal_color)
277 | .titleSelectedColor(R.color.tab_selected_color)
278 | // .marginTop(5)
279 | // .itemPadding(5)
280 | // .unreadNumThreshold(99)
281 | // .unreadTextColor(R.color.white)
282 |
283 | //还有很多属性,详情请查看Builder里面的方法
284 | .create(mNormalIconIds[i], mSelectedIconIds[i], getString(mTitleIds[i]));
285 | return item;
286 | }
287 | ```
288 |
289 | #### 移除条目
290 |
291 | ```
292 | mBottomBarLayout.removeItem(0);
293 | ```
294 |
295 |
296 | #### 开启滑动效果
297 |
298 | 页签之间的切换默认关闭了滑动效果,如果需要开启可以通过调用BottomBarLayout的setSmoothScroll()方法:
299 | ```
300 | mBottomBarLayout.setSmoothScroll(true);
301 | ```
302 |
303 |
304 | 也可以在布局文件中指定BottomBarLayout的smoothScroll属性为true
305 |
306 | 开启后效果如下:
307 |
308 | 
309 |
310 | #### 跳转前拦截
311 | ```
312 | mBottomBarLayout.setOnPageChangeInterceptor(position -> {
313 | if(position == TAB_POSITION_ADD){
314 | //中间凸起图标的位置
315 | Toast.makeText(ViewPager2DemoActivity.this, "可以跳转别的页面,比如发布页", Toast.LENGTH_SHORT).show();
316 | return true; //是否拦截 true拦截不进行跳转 false不拦截
317 | }
318 | boolean isLogin = false; //模拟没有登录
319 | if (position == TAB_POSITION_ME && !isLogin){
320 | //no login intercept to other tab or to LoginActivity
321 | Toast.makeText(ViewPager2DemoActivity.this, "Test intercept, Login first please", Toast.LENGTH_SHORT).show();
322 | return true;
323 | }
324 | return false;
325 | });
326 | ```
327 |
328 | #### 设置条目选中的监听
329 | ```
330 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
331 | //do something
332 | });
333 | ```
334 |
335 | #### 设置同个tab重复点击是否回调setOnItemSelectedListener
336 | ```
337 | app:sameTabClickCallBack="true" //默认为false
338 | ```
339 |
340 |
341 | #### 显示未读数、提示小红点、提示消息
342 |
343 | ```
344 | mBottomBarLayout.setUnread(0,20);//设置第一个页签的未读数为20
345 | mBottomBarLayout.setUnread(1,101);//设置第二个页签的未读数
346 | mBottomBarLayout.showNotify(2);//设置第三个页签显示提示的小红点
347 | mBottomBarLayout.setMsg(3,"NEW");//设置第四个页签显示NEW提示文字
348 | ```
349 |
350 |
351 | 当设置的未读数小于或等于0时,消失未读数的小红点将会消失;
352 | 当未读数为1-99时,则显示对应的数字;
353 | 当未读数大于99时,显示99+;
354 |
355 | #### 设置未读数阈值
356 | 未读数的阈值可以指定BottomBarItem的unreadThreshold属性设置,默认该值为99,如设置 app:unreadThreshold="999" , 若未读数超过该值,则显示"999+"。
357 |
358 | #### 隐藏提示小红点、提示消息
359 |
360 | ```
361 | mBottomBarLayout.hideNotify(2);//隐藏第三个页签显示提示的小红点
362 | mBottomBarLayout.hideMsg(3);//隐藏第四个页签显示的提示文字
363 | ```
364 |
365 |
366 | #### 设置未读数字体颜色
367 |
368 | ```
369 | app:unreadTextColor="@color/unreadTextColor"
370 | ```
371 |
372 |
373 | #### 设置未读数背景
374 |
375 | ```
376 | app:unreadTextBg="@drawable/shape_unread"
377 | ```
378 |
379 |
380 | drawable的编写如下:
381 |
382 | ```
383 |
384 |
385 |
386 |
387 |
388 |
389 | ```
390 |
391 |
392 |
393 | #### 设置提示文字字体颜色、背景
394 | ```
395 | app:msgTextColor="@color/msgTextColor"
396 | app:msgTextBg="@drawable/shape_msg"
397 | ```
398 |
399 | #### 设置提示点背景
400 | ```
401 | app:notifyPointBg="@drawable/shape_notify_point"
402 | ```
403 |
404 |
405 | #### BottomBarItem的介绍
406 | BottomBarItem继承于LinearLayout,其子View有显示图标的ImageView和展示文字的TextView,分别可以通过getImageView()和getTextView()方法获取到对应的子控件。github上不少底部导航栏的控件都没能获取到对应的子控件,所以在需要对子控件进行操作的时候极不方便,有一些的思路并不是用ImageView和TextView,而是用绘制的,所以也不能获取到对应的显示图标的控件或展示文字的控件,造成无法获取到该控件,无法进行一些业务上的操作,比如类似今日头条的底部的首页,点击首页的页签,会更换成加载中的图标,执行旋转动画,BottomBarLayout可以轻松地做到这个需求。
407 |
408 | 演示效果如下:
409 |
410 | 
411 |
412 |
413 | 只需为BottomBarLayout设置页签选中的监听,在回调中进行以下处理:
414 |
415 | mBottomBarLayout.setOnItemSelectedListener(new BottomBarLayout.OnItemSelectedListener() {
416 | @Override
417 | public void onItemSelected(final BottomBarItem bottomBarItem, int previousPosition, final int currentPosition) {
418 | Log.i("MainActivity", "position: " + currentPosition);
419 | if (currentPosition == 0) {
420 | //如果是第一个,即首页
421 | if (previousPosition == currentPosition) {
422 | //如果是在原来位置上点击,更换首页图标并播放旋转动画
423 | if (mRotateAnimation != null && !mRotateAnimation.hasEnded()){
424 | //如果当前动画正在执行
425 | return;
426 | }
427 |
428 | bottomBarItem.setSelectedIcon(R.mipmap.tab_loading);//更换成加载图标
429 |
430 | //播放旋转动画
431 | if (mRotateAnimation == null) {
432 | mRotateAnimation = new RotateAnimation(0, 360,
433 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
434 | 0.5f);
435 | mRotateAnimation.setDuration(800);
436 | mRotateAnimation.setRepeatCount(-1);
437 | }
438 | ImageView bottomImageView = bottomBarItem.getImageView();
439 | bottomImageView.setAnimation(mRotateAnimation);
440 | bottomImageView.startAnimation(mRotateAnimation);//播放旋转动画
441 |
442 | //模拟数据刷新完毕
443 | mHandler.postDelayed(new Runnable() {
444 | @Override
445 | public void run() {
446 | boolean tabNotChanged = mBottomBarLayout.getCurrentItem() == currentPosition; //是否还停留在当前页签
447 | bottomBarItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换成首页原来选中图标
448 | cancelTabLoading(bottomBarItem);
449 | }
450 | }, 3000);
451 | return;
452 | }
453 | }
454 |
455 | //如果点击了其他条目
456 | BottomBarItem bottomItem = mBottomBarLayout.getBottomItem(0);
457 | bottomItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换为原来的图标
458 |
459 | cancelTabLoading(bottomItem);//停止旋转动画
460 | }
461 | });
462 |
463 |
464 | /**停止首页页签的旋转动画*/
465 | private void cancelTabLoading(BottomBarItem bottomItem) {
466 | Animation animation = bottomItem.getImageView().getAnimation();
467 | if (animation != null){
468 | animation.cancel();
469 | }
470 | }
471 |
472 | #### 实现思路:
473 |
474 | 1.当点击页签加载的时候,BottomBarItem通过调用setIconSelectedResourceId()设置成选中状态下的图标资源id为加载中图标的资源id,完成图标的更换操作;
475 |
476 | 2.通过BottomBarItem获取到对应页签的ImageView,对其设置旋转动画,执行旋转动画,当点击其他页签或者数据加载完成后,更换回原来的选中图标,停止旋转动画。
477 |
478 | 好了,到这里BottomBarLayout的介绍就到此为止了,之所以封装这个控件主要是为了方便开发,希望可以帮助到更多人,如果大家有什么想法或者意见不妨向我提出,我会不断完善BottomBarLayout的。
479 |
480 |
481 | #### 支持和鼓励
482 |
483 | 如果觉得我的项目对你有所帮助的话,不妨打赏一下吧!这样我会更加有动力去完善好这个项目:
484 |
485 | 微信赞赏:
486 |
487 | 
488 |
489 |
490 |
--------------------------------------------------------------------------------
/apk/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/apk/demo.apk
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.compile_sdk_version = 28
5 | ext.build_tools_version = "28.0.3"
6 |
7 | ext.min_sdk_version = 16
8 | ext.target_sdk_version = 28
9 |
10 | repositories {
11 | jcenter()
12 | google()
13 | }
14 | dependencies {
15 | classpath 'com.android.tools.build:gradle:4.2.2'
16 |
17 | // NOTE: Do not place your application dependencies here; they belong
18 | // in the individual module build.gradle files
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | jcenter()
25 | google()
26 | maven { url 'https://jitpack.io' }
27 | }
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion compile_sdk_version
5 | buildToolsVersion build_tools_version
6 | defaultConfig {
7 | applicationId "com.chaychan.bottombarlayout"
8 | minSdkVersion min_sdk_version
9 | targetSdkVersion target_sdk_version
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | signingConfigs {
15 | appSign {
16 | keyAlias KEY_ALIAS
17 | keyPassword KEY_PASSWORD
18 | storeFile file(KEY_FILE_PATH)
19 | storePassword KEY_STORE_PASSWORD
20 | }
21 | }
22 |
23 | lintOptions {
24 | checkReleaseBuilds false
25 | abortOnError false
26 | }
27 |
28 | buildTypes {
29 | release {
30 | minifyEnabled false
31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
32 | signingConfig signingConfigs.appSign
33 | }
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation fileTree(include: ['*.jar'], dir: 'libs')
39 | implementation project(':library')
40 | implementation "androidx.appcompat:appcompat:1.2.0"
41 | implementation "androidx.recyclerview:recyclerview:1.1.0"
42 | implementation "androidx.viewpager2:viewpager2:1.0.0"
43 | }
44 |
--------------------------------------------------------------------------------
/demo/demo.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/demo.jks
--------------------------------------------------------------------------------
/demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in G:\SDK\AndroidStudioSDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/demo/src/androidTest/java/com/chaychan/bottombarlayout/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.chaychan.bottombarlayout", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/src/main/assets/cart.json:
--------------------------------------------------------------------------------
1 | {"v":"5.1.16","fr":30,"ip":0,"op":38,"w":48,"h":48,"nm":"icon/Tabbar_静态_01/购物车_48_Sel","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Merged Shape Layer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.11,26.25,0],"ix":2},"a":{"a":0,"k":[23.11,26.25,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[5,5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34.5,40.722],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形备份","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[5,5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[12.5,40.722],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"椭圆形","np":1,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,-2.21],[0.1,-0.35],[0,0],[1.79,0],[0,0]],"o":[[0,0],[2.21,0],[0,0.37],[0,0],[-0.48,1.73],[0,0],[0,0]],"v":[[-14.86,-8],[10.86,-8],[14.86,-4],[14.71,-2.92],[12.46,5.08],[8.61,8],[-14.86,8]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24.86,18.222],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":1,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,-1.06],[0,0],[-2.21,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0.75,0.75],[0,0],[0,2.21],[0,0],[0,0],[0,0]],"v":[[-10.25,-13.75],[-13.75,-13.75],[-11.42,-11.42],[-10.25,-8.59],[-10.25,9.75],[-6.25,13.75],[13.75,13.75],[13.75,13.75]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径 2","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[20.25,20.472],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径 2","np":1,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-24,-24],[24,-24],[24,24],[-24,24]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24,24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":1,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":4,"st":-32,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"路径 3备份","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-2.609,-1.375,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-5.5,0],[5.5,0]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径 3备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[0],"e":[100]},{"t":21}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":38,"st":-32,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"路径 3","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0.891,-7.375,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-9,0],[9,0]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径 3","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13,"s":[0],"e":[100]},{"t":17}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":38,"st":-32,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"形状","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[9.251,1.75,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.38,0],[0,-1.38],[1.38,0],[0,1.38]],"o":[[1.38,0],[0,1.38],[-1.38,0],[0,-1.38]],"v":[[2.14,12],[4.64,14.5],[2.14,17],[-0.36,14.5]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,-0.55],[0.49,-0.05],[0,0],[0,0]],"o":[[0,0],[0.55,0],[0,0.51],[0,0],[0,0],[0,0]],"v":[[-8.36,7],[1.64,7],[2.64,8],[1.76,8.99],[1.64,9],[-8.36,9]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,-2.76],[0.08,-0.36],[0,0],[0,0],[2.15,-0.1],[0,0],[0,0]],"o":[[0,0],[2.76,0],[0,0.37],[0,0],[0,0],[-0.58,2.09],[0,0],[0,0],[0,0]],"v":[[-8.36,-17],[3.36,-17],[8.36,-12],[8.24,-10.91],[8.17,-10.65],[5.92,-2.65],[1.33,1],[1.11,1],[-8.36,1]],"c":true},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.5,1,1,1,1,1,1,1,0,1,0.5,0.5,1,0],"ix":9}},"s":{"a":0,"k":[-13.352,-121.199],"ix":5},"e":{"a":0,"k":[2.423,17],"ix":6},"t":1,"nm":"gradient 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状","np":5,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":38,"st":-32,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"形状","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.107,43.125,0],"ix":2},"a":{"a":0,"k":[-0.003,18.75,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,-6.5]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_-6p5_0p333_0"],"t":3,"s":[80,80,100],"e":[100,100,100]},{"t":5}],"ix":6,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":25,"nm":"投影","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"阴影颜色","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[1,0.2666670084,0.313724994659,0.40000000596],"ix":1}},{"ty":0,"nm":"不透明度","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":102,"ix":2}},{"ty":0,"nm":"方向","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"距离","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":2,"ix":4}},{"ty":0,"nm":"柔和度","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":5,"ix":5}},{"ty":7,"nm":"仅阴影","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.38,0],[0,-1.38],[1.38,0],[0,1.38]],"o":[[1.38,0],[0,1.38],[-1.38,0],[0,-1.38]],"v":[[-10.609,13.75],[-8.109,16.25],[-10.609,18.75],[-13.109,16.25]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-1.38,0],[0,-1.38],[1.38,0],[0,1.38]],"o":[[1.38,0],[0,1.38],[-1.38,0],[0,-1.38]],"v":[[11.391,13.75],[13.891,16.25],[11.391,18.75],[8.891,16.25]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,-0.55],[0.5,-0.05],[0,0],[0,0],[0,0],[-0.2,-0.32],[0,0],[0,-2.76],[0.08,-0.36],[0,0],[0,0],[2.15,-0.1],[0,0],[0,0],[0,0],[-1.58,-0.09],[0,0],[0,0],[0,-0.55],[0.5,-0.05],[0,0],[0,0],[0.11,2.66],[0,0],[0,0],[0.48,0.55],[0,0],[0,0],[-0.82,0.08],[0,0]],"o":[[0.55,0],[0,0.51],[0,0],[0,0],[0,0],[0.27,0.27],[0,0],[2.76,0],[0,0.37],[0,0],[0,0],[-0.59,2.09],[0,0],[0,0],[0,0],[0,1.6],[0,0],[0,0],[0.55,0],[0,0.51],[0,0],[0,0],[-2.69,0],[0,0],[0,0],[0,-0.73],[0,0],[0,0],[-0.6,-0.61],[0,0],[0,0]],"v":[[-13.111,-18.75],[-12.111,-17.75],[-12.991,-16.76],[-13.111,-16.75],[-14.191,-16.75],[-13.571,-16.13],[-12.871,-15.25],[12.609,-15.25],[17.609,-10.25],[17.489,-9.16],[17.429,-8.9],[15.179,-0.9],[10.579,2.75],[10.359,2.75],[-12.111,2.75],[-12.111,5.75],[-9.281,8.74],[-9.111,8.75],[10.889,8.75],[11.889,9.75],[11.009,10.74],[10.889,10.75],[-9.111,10.75],[-14.101,5.97],[-14.111,5.75],[-14.111,-12.59],[-14.851,-14.57],[-14.991,-14.71],[-17.321,-17.04],[-16.721,-18.74],[-16.611,-18.75]],"c":true},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0.447,0.474,0.5,1,0.356,0.393,1,1,0.265,0.312],"ix":9}},"s":{"a":0,"k":[0,-17.677],"ix":5},"e":{"a":0,"k":[0,18.75],"ix":6},"t":1,"nm":"gradient 2","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状","np":5,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":38,"st":-32,"bm":0}],"markers":[{"tm":3,"cm":"1","dr":0},{"tm":27,"cm":"2","dr":0},{"tm":37,"cm":"3","dr":0}]}
--------------------------------------------------------------------------------
/demo/src/main/assets/category.json:
--------------------------------------------------------------------------------
1 | {"v":"5.1.16","fr":30,"ip":0,"op":33,"w":48,"h":48,"nm":"icon/Tabbar_静态_01/分类_48_Sel","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Merged Shape Layer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24,24,0],"ix":2},"a":{"a":0,"k":[24,24,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,3.87],[0,0],[0,0],[0,-3.87],[3.87,0]],"o":[[0,0],[0,0],[3.87,0],[0,3.87],[-3.87,0]],"v":[[-7,0],[-7,-7],[0,-7],[7,0],[0,7]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,34],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份 4","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.87,0],[0,0],[0,0],[-3.87,0],[0,-3.87]],"o":[[0,0],[0,0],[0,-3.87],[3.87,0],[0,3.87]],"v":[[0,7],[-7,7],[-7,0],[0,-7],[7,0]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[34,14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份 3","np":1,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.31],[0,0],[0,0],[0,3.87],[-3.87,0],[0,0]],"o":[[0,0],[0,0],[-3.87,0],[0,-3.87],[0,0],[3.31,0]],"v":[[7,-1],[7,7],[0,7],[-7,0],[0,-7],[1,-7]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[14,14],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份 2","np":1,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.87,0],[0,0],[0,0],[3.87,0],[0,3.87]],"o":[[0,0],[0,0],[0,3.87],[-3.87,0],[0,-3.87]],"v":[[0,-7],[7,-7],[7,0],[0,7],[-7,0]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[14,34],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形备份","np":1,"cix":2,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[48,48],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847000002861,0.847000002861,0.847000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24,24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":0,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":1,"cix":2,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":4,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"路径","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-2.21]],"o":[[2.21,0],[0,0]],"v":[[-2,-2],[2,2]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-90,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":13,"s":[0],"e":[100]},{"t":16}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":33,"st":3,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"形状 - 形状","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1.999,18,0],"ix":2},"a":{"a":0,"k":[-8,18,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-4.42],[4.42,0],[0.14,4.3],[0,0],[0,0],[-0.49,0.05],[0,0]],"o":[[4.42,0],[0,4.42],[-4.33,0],[0,0],[0,0],[0,-0.51],[0,0],[0,0]],"v":[[0,2],[8,10],[0,18],[-8,10.25],[-8,10],[-8,3],[-7.12,2.01],[-7,2]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-4.42,0],[0,-4.42],[4.3,-0.14],[0,0],[0,0],[0.05,0.49],[0,0],[0,0]],"o":[[4.42,0],[0,4.33],[0,0],[0,0],[-0.51,0],[0,0],[0,0],[0,-4.42]],"v":[[0,-18],[8,-10],[0.25,-2],[0,-2],[-7,-2],[-7.99,-2.88],[-8,-3],[-8,-10]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.5,1,1,1,1,1,1,1,0,1,0.5,0.5,1,0],"ix":9}},"s":{"a":0,"k":[-12.777,-128.328],"ix":5},"e":{"a":0,"k":[2.319,18],"ix":6},"t":1,"nm":"gradient 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状","np":4,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":33,"st":3,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"形状 - 形状","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24,42,0],"ix":2},"a":{"a":0,"k":[0,18,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,11]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_11_0p333_0"],"t":3,"s":[80,80,100],"e":[100,100,100]},{"t":5}],"ix":6,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":25,"nm":"投影","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"阴影颜色","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[1,0.2666670084,0.313724994659,0.40000000596],"ix":1}},{"ty":0,"nm":"不透明度","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":102,"ix":2}},{"ty":0,"nm":"方向","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"距离","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":2,"ix":4}},{"ty":0,"nm":"柔和度","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":5,"ix":5}},{"ty":7,"nm":"仅阴影","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.05,-0.49],[0,0],[0,0],[4.42,0],[0,4.42],[-4.3,0.14],[0,0]],"o":[[0.51,0],[0,0],[0,0],[0,4.42],[-4.42,0],[0,-4.33],[0,0],[0,0]],"v":[[-3,2],[-2.01,2.88],[-2,3],[-2,10],[-10,18],[-18,10],[-10.25,2],[-10,2]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,-4.42],[4.42,0],[0.14,4.3],[0,0],[0,0],[-0.49,0.05],[0,0]],"o":[[4.42,0],[0,4.42],[-4.33,0],[0,0],[0,0],[0,-0.51],[0,0],[0,0]],"v":[[10,2],[18,10],[10,18],[2,10.25],[2,10],[2,3],[2.88,2.01],[3,2]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[-0.13,-3.76],[0,0],[0,0],[0.49,-0.05],[0,0],[0,0],[0,4.42],[-4.3,0.14],[0,0]],"o":[[3.79,0],[0,0],[0,0],[0,0.51],[0,0],[0,0],[-4.42,0],[0,-4.33],[0,0],[0,0]],"v":[[-9,-18],[-2,-11.24],[-2,-11],[-2,-3],[-2.88,-2.01],[-3,-2],[-10,-2],[-18,-10],[-10.25,-18],[-10,-18]],"c":true},"ix":2},"nm":"路径 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[-4.42,0],[0,-4.42],[4.3,-0.14],[0,0],[0,0],[0.05,0.49],[0,0],[0,0]],"o":[[4.42,0],[0,4.33],[0,0],[0,0],[-0.51,0],[0,0],[0,0],[0,-4.42]],"v":[[10,-18],[18,-10],[10.25,-2],[10,-2],[3,-2],[2.01,-2.88],[2,-3],[2,-10]],"c":true},"ix":2},"nm":"路径 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0.447,0.474,0.5,1,0.356,0.393,1,1,0.265,0.312],"ix":9}},"s":{"a":0,"k":[0,-16.97],"ix":5},"e":{"a":0,"k":[0,18],"ix":6},"t":1,"nm":"gradient 2","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状","np":6,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":33,"st":3,"bm":0}],"markers":[{"tm":0,"cm":"1","dr":0},{"tm":20,"cm":"2","dr":0},{"tm":33,"cm":"3","dr":0}]}
--------------------------------------------------------------------------------
/demo/src/main/assets/mine.json:
--------------------------------------------------------------------------------
1 | {"v":"5.1.16","fr":30,"ip":0,"op":37,"w":48,"h":48,"nm":"icon/Tabbar_静态_01/我的_48_Sel","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Merged Shape Layer","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24,24,0],"ix":2},"a":{"a":0,"k":[24,24,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.58,0.39],[0,0],[-0.98,0],[0,0],[-0.95,-0.24],[0,0],[-0.23,-1.61],[0,0],[0,0],[0,0]],"o":[[0,0],[0.95,-0.24],[0,0],[0.98,0],[0,0],[1.58,0.39],[0,0],[0,0],[0,0],[0.23,-1.61]],"v":[[-11.39,-2.65],[-3.43,-4.64],[-0.52,-5],[0.52,-5],[3.43,-4.64],[11.39,-2.65],[14.38,0.66],[15,5],[-15,5],[-14.38,0.66]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24,35],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":1,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.97,0],[0,-4.97],[0,0],[4.97,0],[0,4.97],[0,0]],"o":[[4.97,0],[0,0],[0,4.97],[-4.97,0],[0,0],[0,-4.97]],"v":[[0,-10],[9,-1],[9,1],[0,10],[-9,1],[-9,-1]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.172548994422,0.172548994422,0.172548994422,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24,16],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":1,"cix":2,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[48,48],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"矩形路径 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.847000002861,0.847000002861,0.847000002861,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[24,24],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":0,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"矩形","np":1,"cix":2,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":4,"st":-67,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"嘴巴","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,5.384,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.9,0],[-0.58,0.75]],"o":[[0.58,0.75],[0.9,0],[0,0]],"v":[[-2.378,-0.616],[0.002,0.614],[2.382,-0.616]],"c":false},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":2,"lj":2,"nm":"描边 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":17,"s":[0],"e":[100]},{"t":21}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"修剪路径 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":37,"st":-67,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"形状","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_1_0p167_0p167"],"t":13,"s":[0],"e":[-6]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[-6],"e":[6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":17,"s":[6],"e":[0]},{"t":19}],"ix":10,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[0,-12,0],"ix":2},"a":{"a":0,"k":[-8,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.89,-0.19],[0,0],[0,0],[-0.36,-1.91],[0,0],[0,0],[0.56,-0.06],[0,0],[0,0],[0,0]],"o":[[0.91,0],[0,0],[0,0],[1.9,0.47],[0,0],[0,0],[0.08,0.56],[0,0],[0,0],[0,0],[0,0]],"v":[[-7.48,6],[-4.77,6.29],[-4.32,6.39],[3.63,8.38],[7.33,12.3],[7.37,12.52],[7.99,16.86],[7.11,17.99],[7,18],[-8,18],[-8,6]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-0.15,-5.39],[0,0],[0,0],[5.52,0]],"o":[[5.43,0],[0,0],[0,0],[0,5.52],[0,0]],"v":[[-8,-18],[2,-8.28],[2,-8],[2,-6],[-8,4]],"c":true},"ix":2},"nm":"路径 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"合并路径 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.5,1,1,1,1,1,1,1,0,1,0.5,0.5,1,0],"ix":9}},"s":{"a":0,"k":[-12.777,-128.328],"ix":5},"e":{"a":0,"k":[2.319,18],"ix":6},"t":1,"nm":"gradient 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"形状","np":4,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":37,"st":-67,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"头部","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_1_0p167_0p167"],"t":13,"s":[0],"e":[-6]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[-6],"e":[6]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"n":["0p833_0p833_0p333_0"],"t":17,"s":[6],"e":[0]},{"t":19}],"ix":10,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"},"p":{"a":0,"k":[0,-8,0],"ix":2},"a":{"a":0,"k":[0,11,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":25,"nm":"投影","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"阴影颜色","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[1,0.2666670084,0.313724994659,0.40000000596],"ix":1}},{"ty":0,"nm":"不透明度","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":102,"ix":2}},{"ty":0,"nm":"方向","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"距离","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":2,"ix":4}},{"ty":0,"nm":"柔和度","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":5,"ix":5}},{"ty":7,"nm":"仅阴影","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.52,0],[0,-5.52],[0,0],[-5.52,0],[0,5.52],[0,0]],"o":[[-5.52,0],[0,0],[0,5.52],[5.52,0],[0,0],[0,-5.52]],"v":[[0,-11],[-10,-1],[-10,1],[0,11],[10,1],[10,-1]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0.447,0.474,0.5,1,0.356,0.393,1,1,0.265,0.312],"ix":9}},"s":{"a":0,"k":[0,-10.371],"ix":5},"e":{"a":0,"k":[0,11],"ix":6},"t":1,"nm":"gradient 2","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":37,"st":-67,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"身体","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[24,41,0],"ix":2},"a":{"a":0,"k":[0,6,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,-0.818]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p833_0p833_0p333_0","0p833_0p833_0p333_0","0p833_-0p818_0p333_0"],"t":3,"s":[80,80,100],"e":[100,100,100]},{"t":5}],"ix":6,"x":"var $bm_rt;\nvar nearestKeyIndex, nearestKeyIndex, currentTime, currentTime, calculatedVelocity, amplitude, frequency, decay;\n$bm_rt = nearestKeyIndex = 0;\nif (numKeys > 0) {\n $bm_rt = nearestKeyIndex = nearestKey(time).index;\n if (key(nearestKeyIndex).time > time) {\n nearestKeyIndex--;\n }\n}\nif (nearestKeyIndex == 0) {\n $bm_rt = currentTime = 0;\n} else {\n $bm_rt = currentTime = sub(time, key(nearestKeyIndex).time);\n}\nif (nearestKeyIndex > 0 && currentTime < 1) {\n calculatedVelocity = velocityAtTime(sub(key(nearestKeyIndex).time, div(thisComp.frameDuration, 10)));\n amplitude = 0.06;\n frequency = 2;\n decay = 6;\n $bm_rt = sum(value, div(mul(mul(calculatedVelocity, amplitude), Math.sin(mul(mul(mul(frequency, currentTime), 2), Math.PI))), Math.exp(mul(decay, currentTime))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":25,"nm":"投影","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"阴影颜色","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[1,0.2666670084,0.313724994659,0.40000000596],"ix":1}},{"ty":0,"nm":"不透明度","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":102,"ix":2}},{"ty":0,"nm":"方向","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":180,"ix":3}},{"ty":0,"nm":"距离","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":2,"ix":4}},{"ty":0,"nm":"柔和度","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":5,"ix":5}},{"ty":7,"nm":"仅阴影","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.07,0],[0,0],[1.04,-0.26],[0,0],[0.29,-2.01],[0,0],[-0.61,0],[0,0],[0.09,0.6],[0,0],[1.98,0.49],[0,0]],"o":[[0,0],[-1.07,0],[0,0],[-1.98,0.49],[0,0],[-0.09,0.6],[0,0],[0.61,0],[0,0],[-0.29,-2.01],[0,0],[-1.04,-0.26]],"v":[[0.52,-6],[-0.52,-6],[-3.68,-5.61],[-11.63,-3.62],[-15.37,0.52],[-15.99,4.86],[-15,6],[15,6],[15.99,4.86],[15.37,0.52],[11.63,-3.62],[3.68,-5.61]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"g":{"p":3,"k":{"a":0,"k":[0,1,0.447,0.474,0.5,1,0.356,0.393,1,1,0.265,0.312],"ix":9}},"s":{"a":0,"k":[0,-5.657],"ix":5},"e":{"a":0,"k":[0,6],"ix":6},"t":1,"nm":"gradient 3","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"路径","np":2,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":3,"op":37,"st":-67,"bm":0}],"markers":[{"tm":3,"cm":"1","dr":0},{"tm":26,"cm":"2","dr":0},{"tm":36,"cm":"3","dr":0}]}
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/BaseViewPagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Bundle;
4 | import android.view.Menu;
5 | import android.view.MenuItem;
6 |
7 | import com.chaychan.library.BottomBarLayout;
8 | import com.chaychan.library.TabData;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import androidx.appcompat.app.AppCompatActivity;
14 | import androidx.fragment.app.Fragment;
15 | import androidx.fragment.app.FragmentManager;
16 | import androidx.fragment.app.FragmentStatePagerAdapter;
17 | import androidx.viewpager.widget.ViewPager;
18 |
19 | public abstract class BaseViewPagerActivity extends AppCompatActivity {
20 |
21 | private ViewPager mVpContent;
22 | protected BottomBarLayout mBottomBarLayout;
23 |
24 | private List mFragmentList = new ArrayList<>();
25 |
26 | protected abstract String[] getFragmentContents();
27 |
28 | protected abstract List getTabData();
29 |
30 | protected abstract int getLayoutResId();
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(getLayoutResId());
36 |
37 | initView();
38 | initData();
39 | initListener();
40 | }
41 |
42 | public void initView() {
43 | mVpContent = findViewById(R.id.vp_content);
44 | mBottomBarLayout = findViewById(R.id.bbl);
45 | }
46 |
47 | private void initData() {
48 | for (String tabContent : getFragmentContents()) {
49 | TabFragment fragment = new TabFragment();
50 | Bundle bundle = new Bundle();
51 | bundle.putString(TabFragment.CONTENT, tabContent);
52 | fragment.setArguments(bundle);
53 | mFragmentList.add(fragment);
54 | }
55 | mBottomBarLayout.setData(getTabData());
56 | }
57 |
58 | public void initListener() {
59 | mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
60 | mBottomBarLayout.setViewPager(mVpContent);
61 |
62 | mBottomBarLayout.setUnread(0, 20);//设置第一个页签的未读数为20
63 | mBottomBarLayout.setUnread(1, 1001);//设置第二个页签的未读数
64 | mBottomBarLayout.showNotify(2);//设置第三个页签显示提示的小红点
65 | mBottomBarLayout.setMsg(3, "NEW");//设置第四个页签显示NEW提示文字
66 | }
67 |
68 | class MyAdapter extends FragmentStatePagerAdapter {
69 |
70 | public MyAdapter(FragmentManager fm) {
71 | super(fm);
72 | }
73 |
74 | @Override
75 | public Fragment getItem(int position) {
76 | return mFragmentList.get(position);
77 | }
78 |
79 | @Override
80 | public int getCount() {
81 | return mFragmentList.size();
82 | }
83 | }
84 |
85 | @Override
86 | public boolean onCreateOptionsMenu(Menu menu) {
87 | getMenuInflater().inflate(R.menu.menu_demo, menu);
88 | return true;
89 | }
90 |
91 | @Override
92 | public boolean onOptionsItemSelected(MenuItem item) {
93 | int id = item.getItemId();
94 | switch (id) {
95 | case R.id.action_clear_unread:
96 | mBottomBarLayout.setUnread(0, 0);
97 | mBottomBarLayout.setUnread(1, 0);
98 | break;
99 | case R.id.action_clear_notify:
100 | mBottomBarLayout.hideNotify(2);
101 | break;
102 | case R.id.action_clear_msg:
103 | mBottomBarLayout.hideMsg(3);
104 | break;
105 | }
106 | return super.onOptionsItemSelected(item);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/DemoBean.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | public class DemoBean {
4 | public String name;
5 | public Class> clazz;
6 |
7 | public DemoBean(String name, Class> clazz) {
8 | this.name = name;
9 | this.clazz = clazz;
10 | }
11 |
12 | @Override
13 | public String toString() {
14 | return name;
15 | }
16 | }
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/DynamicAddItemActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 | import android.view.Menu;
6 | import android.view.MenuItem;
7 | import android.widget.FrameLayout;
8 |
9 | import com.chaychan.library.BottomBarItem;
10 | import com.chaychan.library.BottomBarLayout;
11 | import com.chaychan.library.UIUtils;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.util.Random;
16 |
17 | import androidx.annotation.NonNull;
18 | import androidx.appcompat.app.AppCompatActivity;
19 | import androidx.fragment.app.FragmentTransaction;
20 |
21 | /**
22 | * @author ChayChan
23 | * @description: 动态添加条目
24 | * @date 2018/12/13 14:45
25 | */
26 | public class DynamicAddItemActivity extends AppCompatActivity {
27 |
28 | private List mFragmentList = new ArrayList<>();
29 | private BottomBarLayout mBottomBarLayout;
30 |
31 | private int[] mNormalIconIds = new int[]{
32 | R.mipmap.tab_home_normal, R.mipmap.tab_video_normal,
33 | R.mipmap.tab_micro_normal, R.mipmap.tab_me_normal
34 | };
35 |
36 | private int[] mSelectedIconIds = new int[]{
37 | R.mipmap.tab_home_selected, R.mipmap.tab_video_selected,
38 | R.mipmap.tab_micro_selected, R.mipmap.tab_me_selected
39 | };
40 |
41 | private int[] mTitleIds = new int[]{
42 | R.string.tab_home,
43 | R.string.tab_video,
44 | R.string.tab_micro,
45 | R.string.tab_me
46 | };
47 |
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_dynamic_add_item);
52 |
53 | initView();
54 | initData();
55 | initListener();
56 | }
57 |
58 | private void initView() {
59 | getSupportActionBar().setTitle(DynamicAddItemActivity.class.getSimpleName());
60 | mBottomBarLayout = findViewById(R.id.bbl);
61 | }
62 |
63 | private void initData() {
64 | for (int i = 0; i < mTitleIds.length; i++) {
65 | //创建item
66 | BottomBarItem item = createBottomBarItem(i);
67 | mBottomBarLayout.addItem(item);
68 |
69 | TabFragment homeFragment = createFragment(mTitleIds[i]);
70 | mFragmentList.add(homeFragment);
71 | }
72 | mBottomBarLayout.setCurrentItem(0);
73 | }
74 |
75 | @NonNull
76 | private TabFragment createFragment(int titleId) {
77 | TabFragment homeFragment = new TabFragment();
78 | Bundle bundle = new Bundle();
79 | bundle.putString(TabFragment.CONTENT, getString(titleId));
80 | homeFragment.setArguments(bundle);
81 | return homeFragment;
82 | }
83 |
84 | private BottomBarItem createBottomBarItem(int i) {
85 | BottomBarItem item = new BottomBarItem.Builder(this)
86 | .titleTextBold(true)
87 | .titleTextSize(UIUtils.dip2Px(this, 8))
88 | .titleNormalColor(getResources().getColor(R.color.tab_normal_color))
89 | .titleSelectedColor(getResources().getColor(R.color.tab_selected_color))
90 | .marginTop(UIUtils.dip2Px(this, -5))
91 | // .itemPadding(5)
92 | // .unreadNumThreshold(99)
93 | // .unreadTextColor(getResources().getColor(R.color.white))
94 |
95 | //还有很多属性,详情请查看Builder里面的方法
96 | //There are still many properties, please see the methods in the Builder for details.
97 | .create(mNormalIconIds[i], mSelectedIconIds[i], getString(mTitleIds[i]));
98 | return item;
99 | }
100 |
101 | private void initListener() {
102 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
103 | Log.i("MainActivity", "position: " + currentPosition);
104 |
105 | changeFragment(currentPosition);
106 | });
107 | }
108 |
109 | private void changeFragment(int currentPosition) {
110 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
111 | transaction.replace(R.id.fl_content, mFragmentList.get(currentPosition));
112 | transaction.commit();
113 | }
114 |
115 | @Override
116 | public boolean onCreateOptionsMenu(Menu menu) {
117 | getMenuInflater().inflate(R.menu.menu_dynamic, menu);
118 | return true;
119 | }
120 |
121 | @Override
122 | public boolean onOptionsItemSelected(MenuItem item) {
123 | int id = item.getItemId();
124 | switch (id) {
125 | case R.id.action_add_item:
126 | int random = new Random().nextInt(3);
127 | //addFragment
128 | mFragmentList.add(createFragment(mTitleIds[random]));
129 | //addItem
130 | BottomBarItem bottomBarItem = createBottomBarItem(random);
131 | mBottomBarLayout.addItem(bottomBarItem);
132 |
133 | mBottomBarLayout.setCurrentItem(mFragmentList.size() - 1);
134 | break;
135 | case R.id.action_remove_item:
136 | //移除条目
137 | mBottomBarLayout.removeItem(0);
138 |
139 | if (mFragmentList.size() != 0) {
140 | mFragmentList.remove(0);
141 |
142 | if (mFragmentList.size() != 0) {
143 | mBottomBarLayout.setCurrentItem(0);
144 | }
145 | }
146 | break;
147 | }
148 | return super.onOptionsItemSelected(item);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/FragmentManagerActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Bundle;
4 | import android.os.Handler;
5 | import android.util.Log;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 | import android.view.animation.Animation;
9 | import android.view.animation.RotateAnimation;
10 | import android.widget.FrameLayout;
11 | import android.widget.ImageView;
12 |
13 | import com.chaychan.library.BottomBarItem;
14 | import com.chaychan.library.BottomBarLayout;
15 | import com.chaychan.library.TabData;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | import androidx.appcompat.app.AppCompatActivity;
21 | import androidx.fragment.app.FragmentTransaction;
22 |
23 | public class FragmentManagerActivity extends AppCompatActivity {
24 |
25 | private List mFragmentList = new ArrayList<>();
26 | private BottomBarLayout mBottomBarLayout;
27 |
28 | private List getTabData() {
29 | List tabData = new ArrayList<>();
30 | tabData.add(new TabData("首页", R.mipmap.tab_home_normal, R.mipmap.tab_home_selected));
31 | tabData.add(new TabData("视频", R.mipmap.tab_video_normal, R.mipmap.tab_video_selected));
32 | tabData.add(new TabData("微头条", R.mipmap.tab_micro_normal, R.mipmap.tab_micro_selected));
33 | tabData.add(new TabData("我的", R.mipmap.tab_me_normal, R.mipmap.tab_me_selected));
34 | return tabData;
35 | }
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_fragment_manager);
41 |
42 | initView();
43 | initData();
44 | initListener();
45 | }
46 |
47 | private void initView() {
48 | getSupportActionBar().setTitle(FragmentManagerActivity.class.getSimpleName());
49 | mBottomBarLayout = findViewById(R.id.bbl);
50 | }
51 |
52 | private void initData() {
53 |
54 | TabFragment homeFragment = new TabFragment();
55 | Bundle bundle1 = new Bundle();
56 | bundle1.putString(TabFragment.CONTENT, "首页");
57 | homeFragment.setArguments(bundle1);
58 | mFragmentList.add(homeFragment);
59 |
60 | TabFragment videoFragment = new TabFragment();
61 | Bundle bundle2 = new Bundle();
62 | bundle2.putString(TabFragment.CONTENT, "视频");
63 | videoFragment.setArguments(bundle2);
64 | mFragmentList.add(videoFragment);
65 |
66 | TabFragment microFragment = new TabFragment();
67 | Bundle bundle3 = new Bundle();
68 | bundle3.putString(TabFragment.CONTENT, "微头条");
69 | microFragment.setArguments(bundle3);
70 | mFragmentList.add(microFragment);
71 |
72 | TabFragment meFragment = new TabFragment();
73 | Bundle bundle4 = new Bundle();
74 | bundle4.putString(TabFragment.CONTENT, "我的");
75 | meFragment.setArguments(bundle4);
76 | mFragmentList.add(meFragment);
77 |
78 | mBottomBarLayout.setData(getTabData());
79 |
80 | changeFragment(0); //默认显示第一页
81 | }
82 |
83 | private void initListener() {
84 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
85 | Log.i("MainActivity", "position: " + currentPosition);
86 | changeFragment(currentPosition);
87 | });
88 |
89 | mBottomBarLayout.setUnread(0, 20);//设置第一个页签的未读数为20
90 | mBottomBarLayout.setUnread(1, 1001);//设置第二个页签的未读数
91 | mBottomBarLayout.showNotify(2);//设置第三个页签显示提示的小红点
92 | mBottomBarLayout.setMsg(3, "NEW");//设置第四个页签显示NEW提示文字
93 | }
94 |
95 | private void changeFragment(int currentPosition) {
96 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
97 | transaction.replace(R.id.fl_content, mFragmentList.get(currentPosition));
98 | transaction.commit();
99 | }
100 |
101 | @Override
102 | public boolean onCreateOptionsMenu(Menu menu) {
103 | getMenuInflater().inflate(R.menu.menu_demo, menu);
104 | return true;
105 | }
106 |
107 | @Override
108 | public boolean onOptionsItemSelected(MenuItem item) {
109 | int id = item.getItemId();
110 | switch (id) {
111 | case R.id.action_clear_unread:
112 | mBottomBarLayout.setUnread(0, 0);
113 | mBottomBarLayout.setUnread(1, 0);
114 | break;
115 | case R.id.action_clear_notify:
116 | mBottomBarLayout.hideNotify(2);
117 | break;
118 | case R.id.action_clear_msg:
119 | mBottomBarLayout.hideMsg(3);
120 | break;
121 | }
122 | return super.onOptionsItemSelected(item);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/LottieDemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import com.chaychan.library.TabData;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * @author ChayChan
10 | * @description: viewPager demo
11 | * @date 2020/11/21 15:18
12 | */
13 | public class LottieDemoActivity extends BaseViewPagerActivity{
14 |
15 | @Override
16 | protected String[] getFragmentContents() {
17 | return new String[]{"首页", "分类", "购物车", "我的"};
18 | }
19 |
20 | @Override
21 | protected List getTabData() {
22 | List tabData = new ArrayList<>();
23 | tabData.add(new TabData(getFragmentContents()[0], "home.json"));
24 | tabData.add(new TabData(getFragmentContents()[1], "category.json"));
25 | tabData.add(new TabData(getFragmentContents()[2], "cart.json"));
26 | tabData.add(new TabData(getFragmentContents()[3], "mine.json"));
27 | return tabData;
28 | }
29 |
30 | @Override
31 | protected int getLayoutResId() {
32 | return R.layout.activity_lottie_demo;
33 | }
34 |
35 | @Override
36 | public void initView() {
37 | super.initView();
38 | getSupportActionBar().setTitle(LottieDemoActivity.class.getSimpleName());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.Gravity;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.appcompat.app.AppCompatActivity;
12 | import androidx.recyclerview.widget.DividerItemDecoration;
13 | import androidx.recyclerview.widget.LinearLayoutManager;
14 | import androidx.recyclerview.widget.RecyclerView;
15 |
16 | import static androidx.recyclerview.widget.RecyclerView.ViewHolder;
17 |
18 | public class MainActivity extends AppCompatActivity {
19 |
20 | private DemoBean[] mDatas= {
21 | new DemoBean("UseWithViewPager2", ViewPager2DemoActivity.class),
22 | new DemoBean("UseWithViewPager", ViewPagerDemoActivity.class),
23 | new DemoBean("UseWithoutViewPager",FragmentManagerActivity.class),
24 | new DemoBean("DynamicAddItem",DynamicAddItemActivity.class),
25 | new DemoBean("UseLottieDemo",LottieDemoActivity.class),
26 | };
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | setContentView(R.layout.activity_main);
32 |
33 | RecyclerView rvList = findViewById(R.id.rv_list);
34 | rvList.setLayoutManager(new LinearLayoutManager(this));
35 | rvList.setHasFixedSize(true);
36 | rvList.setAdapter(new MyRvAdapter());
37 | rvList.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
38 | }
39 |
40 | private class MyRvAdapter extends RecyclerView.Adapter{
41 | @NonNull
42 | @Override
43 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
44 | TextView textView = new TextView(parent.getContext());
45 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
46 | textView.setLayoutParams(params);
47 | textView.setPadding(30,30,30,30);
48 | textView.setTextSize(18);
49 | textView.setGravity(Gravity.CENTER);
50 | textView.setBackground(getResources().getDrawable(R.drawable.selector_bg));
51 | return new MyViewHolder(textView);
52 | }
53 |
54 | @Override
55 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
56 | final DemoBean data = mDatas[position];
57 | ((TextView)holder.itemView).setText(data.name);
58 | holder.itemView.setOnClickListener(v -> v.getContext().startActivity(new Intent(v.getContext(), data.clazz)));
59 | }
60 |
61 | @Override
62 | public int getItemCount() {
63 | return mDatas.length;
64 | }
65 | }
66 |
67 | private class MyViewHolder extends ViewHolder{
68 |
69 | public MyViewHolder(@NonNull View itemView) {
70 | super(itemView);
71 | }
72 | }
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/TabFragment.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Bundle;
4 | import android.view.Gravity;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.TextView;
9 |
10 | import androidx.annotation.Nullable;
11 | import androidx.fragment.app.Fragment;
12 |
13 | /**
14 | * @author ChayChan
15 | * @date 2017/6/23 11:22
16 | */
17 | public class TabFragment extends Fragment {
18 |
19 | public static final String CONTENT = "content";
20 | private TextView mTextView;
21 |
22 |
23 | @Nullable
24 | @Override
25 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle bundle) {
26 | mTextView = new TextView(getActivity());
27 | mTextView.setGravity(Gravity.CENTER);
28 | String content = getArguments().getString(CONTENT);
29 | mTextView.setText(content);
30 | return mTextView;
31 | }
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/ViewPager2DemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 | import android.view.Menu;
6 | import android.view.MenuItem;
7 | import android.widget.Toast;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.appcompat.app.AppCompatActivity;
11 | import androidx.fragment.app.Fragment;
12 | import androidx.fragment.app.FragmentActivity;
13 | import androidx.viewpager2.adapter.FragmentStateAdapter;
14 | import androidx.viewpager2.widget.ViewPager2;
15 |
16 | import com.chaychan.library.BottomBarLayout;
17 | import com.chaychan.library.TabData;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * @author ChayChan
24 | * @description: viewPager2 demo
25 | * @date 2024/07/10 14:58
26 | */
27 | public class ViewPager2DemoActivity extends AppCompatActivity {
28 |
29 | private ViewPager2 mVpContent;
30 | protected BottomBarLayout mBottomBarLayout;
31 |
32 | private List mFragmentList = new ArrayList<>();
33 |
34 | private String[] mTitle = new String[]{"首页", "视频", "微头条", "我的"};
35 |
36 | private List tabData = new ArrayList<>();
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_view_pager2_demo);
42 |
43 | initView();
44 | initData();
45 | initListener();
46 | }
47 |
48 | public void initView() {
49 | mVpContent = findViewById(R.id.vp_content);
50 | mBottomBarLayout = findViewById(R.id.bbl);
51 | }
52 |
53 | private void initData() {
54 | mFragmentList.add(createFragment(mTitle[0]));
55 | mFragmentList.add(createFragment(mTitle[1]));
56 | mFragmentList.add(createFragment("中间add")); //中间凸起占位的fragment 可以是空白的 然后在拦截处理
57 | mFragmentList.add(createFragment(mTitle[2]));
58 | mFragmentList.add(createFragment(mTitle[3]));
59 |
60 | tabData.add(new TabData(mTitle[0], R.mipmap.tab_home_normal, R.mipmap.tab_home_selected));
61 | tabData.add(new TabData(mTitle[1], R.mipmap.tab_video_normal, R.mipmap.tab_video_selected));
62 | tabData.add(new TabData(mTitle[2], R.mipmap.tab_micro_normal, R.mipmap.tab_micro_selected));
63 | tabData.add(new TabData(mTitle[3], R.mipmap.tab_me_normal, R.mipmap.tab_me_selected));
64 | mBottomBarLayout.setData(tabData);
65 | }
66 |
67 | public TabFragment createFragment(String content){
68 | TabFragment fragment = new TabFragment();
69 | Bundle bundle = new Bundle();
70 | bundle.putString(TabFragment.CONTENT, content);
71 | fragment.setArguments(bundle);
72 | return fragment;
73 | }
74 |
75 | public void initListener() {
76 | mVpContent.setAdapter(new MyAdapter(this));
77 | mVpContent.setUserInputEnabled(false);
78 | mBottomBarLayout.setViewPager2(mVpContent);
79 |
80 | mBottomBarLayout.setUnread(0, 20);//设置第一个页签的未读数为20
81 | mBottomBarLayout.setUnread(1, 1001);//设置第二个页签的未读数
82 | mBottomBarLayout.showNotify(3);//设置第三个页签显示提示的小红点
83 | mBottomBarLayout.setMsg(4, "NEW");//设置第四个页签显示NEW提示文字
84 |
85 | mBottomBarLayout.setOnPageChangeInterceptor(position -> {
86 | if(position == 2){
87 | //中间凸起图标的位置
88 | Toast.makeText(ViewPager2DemoActivity.this, "可以跳转别的页面,比如发布页", Toast.LENGTH_SHORT).show();
89 | return true;
90 | }
91 | boolean isLogin = false; //Simulate no login
92 | if (position == 4 && !isLogin){
93 | //no login intercept to other tab or to LoginActivity
94 | Toast.makeText(ViewPager2DemoActivity.this, "Test intercept, Login first please", Toast.LENGTH_SHORT).show();
95 | return true;
96 | }
97 | return false;
98 | });
99 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
100 | Log.i("ViewPager2DemoActivity", "position: " + currentPosition + " pre: " + previousPosition);
101 | });
102 | }
103 |
104 | class MyAdapter extends FragmentStateAdapter {
105 |
106 | public MyAdapter(@NonNull FragmentActivity fragmentActivity) {
107 | super(fragmentActivity);
108 | }
109 |
110 | @NonNull
111 | @Override
112 | public Fragment createFragment(int position) {
113 | return mFragmentList.get(position);
114 | }
115 |
116 | @Override
117 | public int getItemCount() {
118 | return mFragmentList.size();
119 | }
120 | }
121 |
122 | @Override
123 | public boolean onCreateOptionsMenu(Menu menu) {
124 | getMenuInflater().inflate(R.menu.menu_demo, menu);
125 | return true;
126 | }
127 |
128 | @Override
129 | public boolean onOptionsItemSelected(MenuItem item) {
130 | int id = item.getItemId();
131 | switch (id) {
132 | case R.id.action_clear_unread:
133 | mBottomBarLayout.setUnread(0, 0);
134 | mBottomBarLayout.setUnread(1, 0);
135 | break;
136 | case R.id.action_clear_notify:
137 | mBottomBarLayout.hideNotify(3);
138 | break;
139 | case R.id.action_clear_msg:
140 | mBottomBarLayout.hideMsg(4);
141 | break;
142 | }
143 | return super.onOptionsItemSelected(item);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/demo/src/main/java/com/chaychan/bottombarlayout/ViewPagerDemoActivity.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import android.os.Handler;
4 | import android.util.Log;
5 | import android.view.animation.Animation;
6 | import android.view.animation.RotateAnimation;
7 | import android.widget.ImageView;
8 |
9 | import com.chaychan.library.BottomBarItem;
10 | import com.chaychan.library.BottomBarLayout;
11 | import com.chaychan.library.TabData;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | /**
17 | * @author ChayChan
18 | * @description: viewPager demo
19 | * @date 2020/11/21 15:18
20 | */
21 | public class ViewPagerDemoActivity extends BaseViewPagerActivity{
22 |
23 | private RotateAnimation mRotateAnimation;
24 | private Handler mHandler = new Handler();
25 |
26 | @Override
27 | protected String[] getFragmentContents() {
28 | return new String[]{"首页", "视频", "微头条", "我的"};
29 | }
30 |
31 | @Override
32 | protected List getTabData() {
33 | List tabData = new ArrayList<>();
34 | tabData.add(new TabData(getFragmentContents()[0], R.mipmap.tab_home_normal, R.mipmap.tab_home_selected));
35 | tabData.add(new TabData(getFragmentContents()[1], R.mipmap.tab_video_normal, R.mipmap.tab_video_selected));
36 | tabData.add(new TabData(getFragmentContents()[2], R.mipmap.tab_micro_normal, R.mipmap.tab_micro_selected));
37 | tabData.add(new TabData(getFragmentContents()[3], R.mipmap.tab_me_normal, R.mipmap.tab_me_selected));
38 | return tabData;
39 | }
40 |
41 | @Override
42 | protected int getLayoutResId() {
43 | return R.layout.activity_view_pager_demo;
44 | }
45 |
46 | @Override
47 | public void initView() {
48 | super.initView();
49 | getSupportActionBar().setTitle(ViewPagerDemoActivity.class.getSimpleName());
50 | }
51 |
52 | @Override
53 | public void initListener() {
54 | super.initListener();
55 | mBottomBarLayout.setOnItemSelectedListener((bottomBarItem, previousPosition, currentPosition) -> {
56 | Log.i("ViewPagerDemoActivity", "position: " + currentPosition);
57 | if (currentPosition == 0) {
58 | //如果是第一个,即首页
59 | if (previousPosition == currentPosition) {
60 | //如果是在原来位置上点击,更换首页图标并播放旋转动画
61 | if (mRotateAnimation != null && !mRotateAnimation.hasEnded()){
62 | //如果当前动画正在执行
63 | return;
64 | }
65 |
66 | bottomBarItem.setSelectedIcon(R.mipmap.tab_loading);//更换成加载图标
67 |
68 | //播放旋转动画
69 | if (mRotateAnimation == null) {
70 | mRotateAnimation = new RotateAnimation(0, 360,
71 | Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
72 | 0.5f);
73 | mRotateAnimation.setDuration(800);
74 | mRotateAnimation.setRepeatCount(-1);
75 | }
76 | ImageView bottomImageView = bottomBarItem.getImageView();
77 | bottomImageView.setAnimation(mRotateAnimation);
78 | bottomImageView.startAnimation(mRotateAnimation);//播放旋转动画
79 |
80 | //模拟数据刷新完毕
81 | mHandler.postDelayed(() -> {
82 | boolean tabNotChanged = mBottomBarLayout.getCurrentItem() == currentPosition; //是否还停留在当前页签
83 | bottomBarItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换成首页原来选中图标
84 | cancelTabLoading(bottomBarItem);
85 | }, 3000);
86 | return;
87 | }
88 | }
89 |
90 | //如果点击了其他条目
91 | BottomBarItem bottomItem = mBottomBarLayout.getBottomItem(0);
92 | bottomItem.setSelectedIcon(R.mipmap.tab_home_selected);//更换为原来的图标
93 |
94 | cancelTabLoading(bottomItem);//停止旋转动画
95 | });
96 | }
97 |
98 | /**
99 | * 停止首页页签的旋转动画
100 | */
101 | private void cancelTabLoading(BottomBarItem bottomItem) {
102 | Animation animation = bottomItem.getImageView().getAnimation();
103 | if (animation != null) {
104 | animation.cancel();
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/selector_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_dynamic_add_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_fragment_manager.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_lottie_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
27 |
28 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_view_pager2_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_view_pager_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/src/main/res/menu/menu_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/main/res/menu/menu_dynamic.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/icon_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/icon_add.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_home_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_home_normal.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_home_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_home_selected.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_loading.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_me_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_me_normal.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_me_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_me_selected.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_micro_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_micro_normal.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_micro_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_micro_selected.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_video_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_video_normal.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/tab_video_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxhdpi/tab_video_selected.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #515051
8 | #D33D3C
9 |
10 | #00ff00
11 | #00ff00
12 |
13 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BottomBarLayout
3 | 首页
4 | 视频
5 | 微头条
6 | 我的
7 | 分类
8 | 购物车
9 |
10 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/src/test/java/com/chaychan/bottombarlayout/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.bottombarlayout;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | android.useAndroidX=true
19 |
20 | KEY_FILE_PATH=demo.jks
21 | KEY_PASSWORD=123456
22 | KEY_ALIAS=demo
23 | KEY_STORE_PASSWORD=123456
24 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jun 25 09:11:31 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-6.7.1-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/intro_img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/4.png
--------------------------------------------------------------------------------
/intro_img/display1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/display1.gif
--------------------------------------------------------------------------------
/intro_img/display2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/display2.gif
--------------------------------------------------------------------------------
/intro_img/display3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/display3.gif
--------------------------------------------------------------------------------
/intro_img/download_qr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/download_qr.png
--------------------------------------------------------------------------------
/intro_img/float.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/float.gif
--------------------------------------------------------------------------------
/intro_img/lottie.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/lottie.gif
--------------------------------------------------------------------------------
/intro_img/transfer_code.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chaychan/BottomBarLayout/502ab812eabcab63ec55c5ed4baa746f40e41343/intro_img/transfer_code.jpg
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion compile_sdk_version
5 | buildToolsVersion build_tools_version
6 |
7 | defaultConfig {
8 | minSdkVersion min_sdk_version
9 | targetSdkVersion target_sdk_version
10 | versionCode 6
11 | versionName "1.2.1"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(include: ['*.jar'], dir: 'libs')
26 | implementation "androidx.appcompat:appcompat:1.2.0"
27 | implementation "com.airbnb.android:lottie:3.5.0"
28 | implementation "androidx.viewpager2:viewpager2:1.0.0"
29 | }
30 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in G:\SDK\AndroidStudioSDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/library/src/androidTest/java/com/chaychan/library/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.chaychan.library.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/library/src/main/java/com/chaychan/library/BottomBarItem.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.drawable.Drawable;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.util.TypedValue;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.widget.FrameLayout;
12 | import android.widget.ImageView;
13 | import android.widget.LinearLayout;
14 | import android.widget.TextView;
15 |
16 | import com.airbnb.lottie.LottieAnimationView;
17 |
18 | import java.util.Locale;
19 |
20 | import androidx.annotation.NonNull;
21 | import androidx.annotation.Nullable;
22 |
23 |
24 | /**
25 | * @author ChayChan
26 | * @description: 底部tab条目
27 | * @date 2017/6/23 9:14
28 | */
29 |
30 | public class BottomBarItem extends LinearLayout {
31 |
32 | private Context context;
33 | private ImageView mImageView;
34 | private LottieAnimationView mLottieView;
35 | private TextView mTvUnread;
36 | private TextView mTvNotify;
37 | private TextView mTvMsg;
38 | private TextView mTextView;
39 |
40 | private Builder mBuilder;
41 |
42 | public BottomBarItem(Context context) {
43 | super(context);
44 | }
45 |
46 | public BottomBarItem(Context context, @Nullable AttributeSet attrs) {
47 | this(context, attrs, 0);
48 | }
49 |
50 | public BottomBarItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
51 | super(context, attrs, defStyleAttr);
52 | this.context = context;
53 | checkValues();//检查值是否合法
54 | init();//初始化相关操作
55 | }
56 |
57 | /**
58 | * 检查传入的值是否完善
59 | */
60 | private void checkValues() {
61 | if (mBuilder == null){
62 | throw new IllegalStateException("Builder is null");
63 | }
64 |
65 | if (mBuilder.unreadTextBg == null) {
66 | mBuilder.unreadTextBg = getResources().getDrawable(R.drawable.shape_unread);
67 | }
68 |
69 | if (mBuilder.msgTextBg == null) {
70 | mBuilder.msgTextBg = getResources().getDrawable(R.drawable.shape_msg);
71 | }
72 |
73 | if (mBuilder.notifyPointBg == null) {
74 | mBuilder.notifyPointBg = getResources().getDrawable(R.drawable.shape_notify_point);
75 | }
76 | }
77 |
78 | private void init() {
79 | setOrientation(VERTICAL);
80 | setGravity(Gravity.CENTER);
81 |
82 | View view = initView();
83 |
84 | FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mImageView.getLayoutParams();
85 | if (mBuilder.iconWidth != 0 && mBuilder.iconHeight != 0) {
86 | //如果有设置图标的宽度和高度,则设置ImageView的宽高
87 | layoutParams.width = mBuilder.iconWidth;
88 | layoutParams.height = mBuilder.iconHeight;
89 | }
90 |
91 | if (!TextUtils.isEmpty(mBuilder.lottieJson)){
92 | mLottieView.setLayoutParams(layoutParams);
93 | mLottieView.setAnimation(mBuilder.lottieJson);
94 | mLottieView.setRepeatCount(0);
95 | }else{
96 | mImageView.setImageDrawable(mBuilder.normalIcon);
97 | mImageView.setLayoutParams(layoutParams);
98 | }
99 |
100 | mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mBuilder.titleTextSize);//设置底部文字字体大小
101 | mTextView.getPaint().setFakeBoldText(mBuilder.titleTextBold);
102 | mTvUnread.setTextSize(TypedValue.COMPLEX_UNIT_PX, mBuilder.unreadTextSize);//设置未读数的字体大小
103 | mTvUnread.setTextColor(mBuilder.unreadTextColor);//设置未读数字体颜色
104 | mTvUnread.setBackground(mBuilder.unreadTextBg);//设置未读数背景
105 |
106 | mTvMsg.setTextSize(TypedValue.COMPLEX_UNIT_PX, mBuilder.msgTextSize);//设置提示文字的字体大小
107 | mTvMsg.setTextColor(mBuilder.msgTextColor);//设置提示文字的字体颜色
108 | mTvMsg.setBackground(mBuilder.msgTextBg);//设置提示文字的背景颜色
109 |
110 | mTvNotify.setBackground(mBuilder.notifyPointBg);//设置提示点的背景颜色
111 |
112 | mTextView.setTextColor(mBuilder.titleNormalColor);//设置底部文字字体颜色
113 | mTextView.setText(mBuilder.title);//设置标签文字
114 |
115 | LayoutParams textLayoutParams = (LayoutParams) mTextView.getLayoutParams();
116 | textLayoutParams.topMargin = mBuilder.marginTop;
117 | mTextView.setLayoutParams(textLayoutParams);
118 |
119 | addView(view);
120 | }
121 |
122 | @NonNull
123 | private View initView() {
124 | View view = View.inflate(context, R.layout.item_bottom_bar, null);
125 | if (mBuilder.itemPadding != 0) {
126 | //如果有设置item的padding
127 | view.setPadding(mBuilder.itemPadding, mBuilder.itemPadding, mBuilder.itemPadding, mBuilder.itemPadding);
128 | }
129 | mImageView = view.findViewById(R.id.iv_icon);
130 | mLottieView = view.findViewById(R.id.lottieView);
131 | mTvUnread = view.findViewById(R.id.tv_unred_num);
132 | mTvMsg = view.findViewById(R.id.tv_msg);
133 | mTvNotify = view.findViewById(R.id.tv_point);
134 | mTextView = view.findViewById(R.id.tv_text);
135 |
136 | mImageView.setVisibility(!TextUtils.isEmpty(mBuilder.lottieJson) ? GONE : VISIBLE);
137 | mLottieView.setVisibility(!TextUtils.isEmpty(mBuilder.lottieJson) ? VISIBLE : GONE);
138 |
139 | return view;
140 | }
141 |
142 | public String getTitle(){
143 | return mBuilder.title;
144 | }
145 |
146 | public ImageView getImageView() {
147 | return mImageView;
148 | }
149 |
150 | public TextView getTextView() {
151 | return mTextView;
152 | }
153 |
154 | public void setNormalIcon(Drawable normalIcon) {
155 | mBuilder.normalIcon = normalIcon;
156 | refreshTab();
157 | }
158 |
159 | public void setNormalIcon(int resId) {
160 | setNormalIcon(UIUtils.getDrawable(context, resId));
161 | }
162 |
163 | public void setSelectedIcon(Drawable selectedIcon) {
164 | mBuilder.selectedIcon = selectedIcon;
165 | refreshTab();
166 | }
167 |
168 | public void setSelectedIcon(int resId) {
169 | setSelectedIcon(UIUtils.getDrawable(context, resId));
170 | }
171 |
172 | public void refreshTab(boolean isSelected) {
173 | setSelected(isSelected);
174 | refreshTab();
175 | }
176 |
177 | public void refreshTab() {
178 | if (!TextUtils.isEmpty(mBuilder.lottieJson)){
179 | if (isSelected()){
180 | mLottieView.playAnimation();
181 | }else{
182 | //取消动画 进度设置为0
183 | mLottieView.cancelAnimation();
184 | mLottieView.setProgress(0);
185 | }
186 | }else{
187 | mImageView.setImageDrawable(isSelected() ? mBuilder.selectedIcon : mBuilder.normalIcon);
188 | }
189 |
190 | mTextView.setTextColor(isSelected() ? mBuilder.titleSelectedColor : mBuilder.titleNormalColor);
191 | }
192 |
193 | private void setTvVisible(TextView tv) {
194 | //都设置为不可见
195 | mTvUnread.setVisibility(GONE);
196 | mTvMsg.setVisibility(GONE);
197 | mTvNotify.setVisibility(GONE);
198 |
199 | tv.setVisibility(VISIBLE);//设置为可见
200 | }
201 |
202 | public int getUnreadNumThreshold() {
203 | return mBuilder.unreadNumThreshold;
204 | }
205 |
206 | public void setUnreadNumThreshold(int unreadNumThreshold) {
207 | mBuilder.unreadNumThreshold = unreadNumThreshold;
208 | }
209 |
210 | public void setUnreadNum(int unreadNum) {
211 | setTvVisible(mTvUnread);
212 | if (unreadNum <= 0) {
213 | mTvUnread.setVisibility(GONE);
214 | } else if (unreadNum <= mBuilder.unreadNumThreshold) {
215 | mTvUnread.setText(String.valueOf(unreadNum));
216 | } else {
217 | mTvUnread.setText(String.format(Locale.CHINA, "%d+", mBuilder.unreadNumThreshold));
218 | }
219 | }
220 |
221 | public void setMsg(String msg) {
222 | setTvVisible(mTvMsg);
223 | mTvMsg.setText(msg);
224 | }
225 |
226 | public void hideMsg() {
227 | mTvMsg.setVisibility(GONE);
228 | }
229 |
230 | public void showNotify() {
231 | setTvVisible(mTvNotify);
232 | }
233 |
234 | public void hideNotify() {
235 | mTvNotify.setVisibility(GONE);
236 | }
237 |
238 | public BottomBarItem create(Builder builder) {
239 | this.context = builder.context;
240 | mBuilder = builder;
241 | checkValues();
242 | init();
243 | return this;
244 | }
245 |
246 | public static final class Builder {
247 | private Context context;
248 | private Drawable normalIcon;//普通状态图标的资源id
249 | private Drawable selectedIcon;//选中状态图标的资源id
250 | private String title;//标题
251 | private boolean titleTextBold;//文字加粗
252 | private int titleTextSize;//字体大小
253 | private int titleNormalColor; //描述文本的默认显示颜色
254 | private int titleSelectedColor; //述文本的默认选中显示颜色
255 | private int marginTop;//文字和图标的距离
256 | private int iconWidth;//图标的宽度
257 | private int iconHeight;//图标的高度
258 | private int itemPadding;//BottomBarItem的padding
259 | private int unreadTextSize; //未读数字体大小
260 | private int unreadNumThreshold;//未读数阈值
261 | private int unreadTextColor;//未读数字体颜色
262 | private Drawable unreadTextBg;//未读数文字背景
263 | private int msgTextSize; //消息字体大小
264 | private int msgTextColor;//消息文字颜色
265 | private Drawable msgTextBg;//消息提醒背景颜色
266 | private Drawable notifyPointBg;//小红点背景颜色
267 | private String lottieJson; //lottie文件名
268 |
269 | public Builder(Context context) {
270 | this.context = context;
271 | titleTextBold = false;
272 | titleTextSize = UIUtils.sp2px(context, 12);
273 | titleNormalColor = getColor(R.color.bbl_999999);
274 | titleSelectedColor = getColor(R.color.bbl_ff0000);
275 | unreadTextSize = UIUtils.sp2px(context, 10);
276 | msgTextSize = UIUtils.sp2px(context, 6);
277 | unreadTextColor = getColor(R.color.white);
278 | unreadNumThreshold = 99;
279 | msgTextColor = getColor(R.color.white);
280 | }
281 |
282 | /**
283 | * Sets the default icon's resourceId
284 | */
285 | public Builder normalIcon(Drawable normalIcon) {
286 | this.normalIcon = normalIcon;
287 | return this;
288 | }
289 |
290 | /**
291 | * Sets the selected icon's resourceId
292 | */
293 | public Builder selectedIcon(Drawable selectedIcon) {
294 | this.selectedIcon = selectedIcon;
295 | return this;
296 | }
297 |
298 | /**
299 | * Sets the title's resourceId
300 | */
301 | public Builder title(int titleId) {
302 | this.title = context.getString(titleId);
303 | return this;
304 | }
305 |
306 | /**
307 | * Sets the title string
308 | */
309 | public Builder title(String title) {
310 | this.title = title;
311 | return this;
312 | }
313 |
314 | /**
315 | * Sets the title's text bold
316 | */
317 | public Builder titleTextBold(boolean titleTextBold) {
318 | this.titleTextBold = titleTextBold;
319 | return this;
320 | }
321 |
322 | /**
323 | * Sets the title's text size
324 | */
325 | public Builder titleTextSize(int titleTextSize) {
326 | this.titleTextSize = titleTextSize;
327 | return this;
328 | }
329 |
330 | /**
331 | * Sets the title's normal color resourceId
332 | */
333 | public Builder titleNormalColor(int titleNormalColor) {
334 | this.titleNormalColor = titleNormalColor;
335 | return this;
336 | }
337 |
338 | /**
339 | * Sets the title's selected color resourceId
340 | */
341 | public Builder titleSelectedColor(int titleSelectedColor) {
342 | this.titleSelectedColor = titleSelectedColor;
343 | return this;
344 | }
345 |
346 | /**
347 | * Sets the item's margin top
348 | */
349 | public Builder marginTop(int marginTop) {
350 | this.marginTop = marginTop;
351 | return this;
352 | }
353 |
354 | /**
355 | * Sets icon's width
356 | */
357 | public Builder iconWidth(int iconWidth) {
358 | this.iconWidth = iconWidth;
359 | return this;
360 | }
361 |
362 | /**
363 | * Sets icon's height
364 | */
365 | public Builder iconHeight(int iconHeight) {
366 | this.iconHeight = iconHeight;
367 | return this;
368 | }
369 |
370 |
371 | /**
372 | * Sets padding for item
373 | */
374 | public Builder itemPadding(int itemPadding) {
375 | this.itemPadding = itemPadding;
376 | return this;
377 | }
378 |
379 | /**
380 | * Sets unread font size
381 | */
382 | public Builder unreadTextSize(int unreadTextSize) {
383 | this.unreadTextSize = unreadTextSize;
384 | return this;
385 | }
386 |
387 | /**
388 | * Sets the number of unread array thresholds greater than the threshold to be displayed as n + n as the set threshold
389 | */
390 | public Builder unreadNumThreshold(int unreadNumThreshold) {
391 | this.unreadNumThreshold = unreadNumThreshold;
392 | return this;
393 | }
394 |
395 | /**
396 | * Sets the message font size
397 | */
398 | public Builder msgTextSize(int msgTextSize) {
399 | this.msgTextSize = msgTextSize;
400 | return this;
401 | }
402 |
403 | /**
404 | * Sets the message font background
405 | */
406 | public Builder unreadTextBg(Drawable unreadTextBg) {
407 | this.unreadTextBg = unreadTextBg;
408 | return this;
409 | }
410 |
411 | /**
412 | * Sets unread font color
413 | */
414 | public Builder unreadTextColor(int unreadTextColor) {
415 | this.unreadTextColor = unreadTextColor;
416 | return this;
417 | }
418 |
419 | /**
420 | * Sets the message font color
421 | */
422 | public Builder msgTextColor(int msgTextColor) {
423 | this.msgTextColor =msgTextColor;
424 | return this;
425 | }
426 |
427 | /**
428 | * Sets the message font background
429 | */
430 | public Builder msgTextBg(Drawable msgTextBg) {
431 | this.msgTextBg = msgTextBg;
432 | return this;
433 | }
434 |
435 | /**
436 | * Set the message prompt point background
437 | */
438 | public Builder notifyPointBg(Drawable notifyPointBg) {
439 | this.notifyPointBg = notifyPointBg;
440 | return this;
441 | }
442 |
443 | /**
444 | * Set the name of lottie json file
445 | */
446 | public Builder lottieJson(String lottieJson) {
447 | this.lottieJson = lottieJson;
448 | return this;
449 | }
450 |
451 | /**
452 | * Create a BottomBarItem object
453 | */
454 | public BottomBarItem create(Drawable normalIcon, Drawable selectedIcon, String text) {
455 | this.normalIcon = normalIcon;
456 | this.selectedIcon = selectedIcon;
457 | title = text;
458 |
459 | BottomBarItem bottomBarItem = new BottomBarItem(context);
460 | return bottomBarItem.create(this);
461 | }
462 |
463 | public BottomBarItem create(int normalIconId, int selectedIconId, String text) {
464 | return create(UIUtils.getDrawable(context, normalIconId), UIUtils.getDrawable(context, selectedIconId), text);
465 | }
466 |
467 | private int getColor(int colorId) {
468 | return context.getResources().getColor(colorId);
469 | }
470 |
471 | }
472 | }
473 |
--------------------------------------------------------------------------------
/library/src/main/java/com/chaychan/library/BottomBarLayout.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.drawable.Drawable;
6 | import android.text.TextUtils;
7 | import android.util.AttributeSet;
8 | import android.util.Log;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.widget.FrameLayout;
12 | import android.widget.LinearLayout;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | import androidx.viewpager.widget.ViewPager;
18 | import androidx.viewpager2.widget.ViewPager2;
19 |
20 | /**
21 | * @author ChayChan
22 | * @description: 底部页签根节点
23 | * @date 2017/6/23 11:02
24 | */
25 | public class BottomBarLayout extends FrameLayout implements ViewPager.OnPageChangeListener {
26 |
27 | private boolean titleTextBold = false;//文字加粗
28 | private int titleTextSize = 12;//文字大小 默认为12sp
29 | private int titleNormalColor; //描述文本的默认显示颜色
30 | private int titleSelectedColor; //述文本的默认选中显示颜色
31 | private int marginTop = 0;//文字和图标的距离,默认0dp
32 | private int iconWidth;//图标的宽度
33 | private int iconHeight;//图标的高度
34 | private int itemPadding;//BottomBarItem的padding
35 | private int unreadTextSize = 10; //未读数默认字体大小10sp
36 | private int unreadNumThreshold = 99;//未读数阈值
37 | private int unreadTextColor;//未读数字体颜色
38 | private Drawable unreadTextBg;//未读数字体背景
39 | private int msgTextSize = 6; //消息默认字体大小6sp
40 | private int msgTextColor;//消息文字颜色
41 | private Drawable msgTextBg;//消息文字背景
42 | private Drawable notifyPointBg;//小红点背景
43 |
44 | private Drawable barBackground;
45 | private int barHeight = 45;// bar的高度
46 |
47 | private Drawable floatIcon; //凸起图标
48 | private boolean floatEnable; //是否中间图标凸起
49 | private int floatMarginBottom = 0;//凸起按钮底部间距
50 | private int floatIconWidth; //凸起图标的宽度
51 | private int floatIconHeight; //凸起图标的高度
52 |
53 | private ViewPager mViewPager;
54 | private List mItemViews = new ArrayList<>();
55 | private int mCurrentItem;//当前条目的索引
56 | private boolean mSmoothScroll;
57 |
58 | //相同tab点击是否回调
59 | private boolean mSameTabClickCallBack;
60 |
61 | private ViewPager2 mViewPager2;
62 |
63 | private LinearLayout mLlTab;
64 |
65 | public BottomBarLayout(Context context) {
66 | this(context, null);
67 | }
68 |
69 | public BottomBarLayout(Context context, AttributeSet attrs) {
70 | this(context, attrs, 0);
71 | }
72 |
73 | public BottomBarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
74 | super(context, attrs, defStyleAttr);
75 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BottomBarLayout);
76 | initAttrs(ta, context);
77 | mLlTab = new LinearLayout(context);
78 | mLlTab.setOrientation(LinearLayout.HORIZONTAL);
79 | if (barBackground != null){
80 | mLlTab.setBackground(barBackground);
81 | }else{
82 | mLlTab.setBackgroundColor(UIUtils.getColor(context, R.color.tab_gb));
83 | }
84 | addView(mLlTab);
85 | ta.recycle();
86 | }
87 |
88 | @Override
89 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
90 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
91 | Log.i("bottomBarLayout", "width: " + getMeasuredWidth() + " height: " + barHeight);
92 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(getMeasuredWidth(), barHeight);
93 | params.gravity = Gravity.BOTTOM;
94 | mLlTab.setLayoutParams(params);
95 | }
96 |
97 | private void initAttrs(TypedArray ta, Context context) {
98 | mSmoothScroll = ta.getBoolean(R.styleable.BottomBarLayout_smoothScroll, mSmoothScroll);
99 | mSameTabClickCallBack = ta.getBoolean(R.styleable.BottomBarLayout_sameTabClickCallBack, mSameTabClickCallBack);
100 | barBackground = ta.getDrawable(R.styleable.BottomBarLayout_barBackground);
101 | barHeight = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_barHeight, UIUtils.dip2Px(context, barHeight));
102 | floatEnable = ta.getBoolean(R.styleable.BottomBarLayout_floatEnable, floatEnable);
103 | floatIcon = ta.getDrawable(R.styleable.BottomBarLayout_floatIcon);
104 | floatMarginBottom = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_floatMarginBottom, UIUtils.dip2Px(context, floatMarginBottom));
105 | floatIconWidth = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_floatIconWidth, UIUtils.dip2Px(context, floatIconWidth));
106 | floatIconHeight = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_floatIconHeight, UIUtils.dip2Px(context, floatIconHeight));
107 |
108 |
109 | titleTextBold = ta.getBoolean(R.styleable.BottomBarLayout_itemTextBold, titleTextBold);
110 | titleTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_itemTextSize, UIUtils.sp2px(context, titleTextSize));
111 |
112 | titleNormalColor = ta.getColor(R.styleable.BottomBarLayout_textColorNormal, UIUtils.getColor(context, R.color.bbl_999999));
113 | titleSelectedColor = ta.getColor(R.styleable.BottomBarLayout_textColorSelected, UIUtils.getColor(context, R.color.bbl_ff0000));
114 |
115 | marginTop = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_itemMarginTop, UIUtils.dip2Px(context, marginTop));
116 |
117 | iconWidth = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_iconWidth, 0);
118 | iconHeight = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_iconHeight, 0);
119 | itemPadding = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_itemPadding, 0);
120 |
121 | unreadTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_unreadTextSize, UIUtils.sp2px(context, unreadTextSize));
122 | unreadTextColor = ta.getColor(R.styleable.BottomBarLayout_unreadTextColor, UIUtils.getColor(context, R.color.white));
123 | unreadTextBg = ta.getDrawable(R.styleable.BottomBarLayout_unreadTextBg);
124 |
125 | msgTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarLayout_msgTextSize, UIUtils.sp2px(context, msgTextSize));
126 | msgTextColor = ta.getColor(R.styleable.BottomBarLayout_msgTextColor, UIUtils.getColor(context, R.color.white));
127 | msgTextBg = ta.getDrawable(R.styleable.BottomBarLayout_msgTextBg);
128 |
129 | notifyPointBg = ta.getDrawable(R.styleable.BottomBarLayout_notifyPointBg);
130 |
131 | unreadNumThreshold = ta.getInteger(R.styleable.BottomBarLayout_unreadThreshold, unreadNumThreshold);
132 | }
133 |
134 | public void setViewPager(ViewPager viewPager) {
135 | this.mViewPager = viewPager;
136 |
137 | if (mViewPager != null) {
138 | mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
139 | @Override
140 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
141 | }
142 | @Override
143 | public void onPageSelected(int position) {
144 | handlePageSelected(position);
145 | }
146 | @Override
147 | public void onPageScrollStateChanged(int state) {
148 | }
149 | });
150 | }
151 | }
152 |
153 | public void setViewPager2(androidx.viewpager2.widget.ViewPager2 viewPager2) {
154 | this.mViewPager2 = viewPager2;
155 |
156 | if (mViewPager2 != null) {
157 | mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
158 | @Override
159 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
160 | }
161 | @Override
162 | public void onPageSelected(int position) {
163 | handlePageSelected(position);
164 | }
165 | @Override
166 | public void onPageScrollStateChanged(int state) {
167 | }
168 | });
169 | }
170 | }
171 |
172 | private BottomBarItem createBottomBarItem(Drawable normalIcon, Drawable selectedIcon, String title, int iconWidth, int iconHeight, String lottieJson){
173 | return new BottomBarItem.Builder(getContext())
174 | .titleTextBold(titleTextBold)
175 | .titleTextSize(titleTextSize)
176 | .titleNormalColor(titleNormalColor)
177 | .iconHeight(iconHeight)
178 | .iconWidth(iconWidth)
179 | .marginTop(marginTop)
180 | .itemPadding(itemPadding)
181 | .titleSelectedColor(titleSelectedColor)
182 | .lottieJson(lottieJson)
183 | .unreadNumThreshold(unreadNumThreshold)
184 | .unreadTextBg(unreadTextBg)
185 | .unreadTextSize(unreadTextSize)
186 | .unreadTextColor(unreadTextColor)
187 | .msgTextBg(msgTextBg)
188 | .msgTextColor(msgTextColor)
189 | .msgTextSize(msgTextSize)
190 | .notifyPointBg(notifyPointBg)
191 | .create(normalIcon, selectedIcon, title);
192 | }
193 |
194 | public void setData(List tabData){
195 | if (tabData == null || tabData.size() == 0){
196 | throw new IllegalArgumentException("tabData is null");
197 | }
198 | mItemViews.clear();
199 | mLlTab.removeAllViews();
200 |
201 | //添加tab
202 | for (int i = 0; i < tabData.size(); i++) {
203 | TabData itemData = tabData.get(i);
204 | Drawable normalIcon = !TextUtils.isEmpty(itemData.getLottieJson()) ? null : itemData.getNormalIcon() != null ? itemData.getNormalIcon() : getContext().getResources().getDrawable(itemData.getNormalIconResId());
205 | Drawable selectedIcon = !TextUtils.isEmpty(itemData.getLottieJson()) ? null : itemData.getSelectedIcon() != null ? itemData.getSelectedIcon() : getContext().getResources().getDrawable(itemData.getSelectedIconResId());
206 | int iconWidth = itemData.getIconWidth() == 0 ? this.iconWidth : itemData.getIconWidth();
207 | int iconHeight = itemData.getIconHeight() == 0 ? this.iconHeight : itemData.getIconHeight();
208 | BottomBarItem item = createBottomBarItem(normalIcon, selectedIcon, itemData.getTitle(), iconWidth, iconHeight, itemData.getLottieJson());
209 | addItem(item);
210 | }
211 |
212 | //如果开启凸起 且是 其他tab总数是偶数
213 | if (floatEnable && tabData.size() % 2 == 0){
214 | BottomBarItem item = createBottomBarItem(floatIcon, floatIcon, "", floatIconWidth, floatIconHeight, "");
215 | addItem(item, (tabData.size() + 1) / 2, true);
216 | }
217 |
218 | mItemViews.get(0).refreshTab(true);
219 | }
220 |
221 | public void addItem(BottomBarItem item){
222 | addItem(item, -1, false);
223 | }
224 |
225 | public void addItem(BottomBarItem item, int index, boolean isFloatItem) {
226 | if (index == -1){
227 | mItemViews.add(item);
228 | }else{
229 | mItemViews.add(index, item);
230 | }
231 |
232 | int position = index != -1 ? index : mItemViews.size() - 1;
233 | Log.e("bottomBarLayout", "position: " + position);
234 |
235 | View view = item;
236 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT);
237 | layoutParams.weight = 1;
238 | layoutParams.gravity = Gravity.CENTER;
239 | view.setLayoutParams(layoutParams);
240 |
241 | if (isFloatItem){
242 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(floatIconWidth, floatIconHeight);
243 | params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
244 | params.bottomMargin = floatMarginBottom;
245 | addView(item, params);
246 | view = new View(getContext());
247 | }
248 |
249 | mLlTab.addView(view, position, layoutParams);
250 |
251 | //tab添加点击事件
252 | for (int i = 0; i < mItemViews.size(); i++) {
253 | mItemViews.get(i).setOnClickListener(new MyOnClickListener(i));
254 | }
255 | }
256 |
257 | public void removeItem(int position) {
258 | if (position >= 0 && position < mItemViews.size()) {
259 | BottomBarItem item = mItemViews.get(position);
260 | if (mItemViews.contains(item)) {
261 | resetState();
262 | mLlTab.removeViewAt(position);
263 | }
264 | mItemViews.remove(item);
265 |
266 | //tab添加点击事件
267 | for (int i = 0; i < mItemViews.size(); i++) {
268 | mItemViews.get(i).setOnClickListener(new MyOnClickListener(i));
269 | }
270 | }
271 | }
272 |
273 | private void handlePageSelected(int position){
274 | //滑动时判断是否需要拦截跳转
275 | if (mOnPageChangeInterceptor != null
276 | && mOnPageChangeInterceptor.onIntercepted(position)){
277 | setCurrentItem(mCurrentItem);
278 | return;
279 | }
280 | resetState();
281 | mItemViews.get(position).refreshTab(true);
282 | int prePos = mCurrentItem;
283 | mCurrentItem = position;//记录当前位置
284 | if (onItemSelectedListener != null) {
285 | onItemSelectedListener.onItemSelected(getBottomItem(mCurrentItem), prePos, mCurrentItem);
286 | }
287 | }
288 |
289 | @Override
290 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
291 |
292 | }
293 |
294 | @Override
295 | public void onPageSelected(int position) {
296 | handlePageSelected(position);
297 | }
298 |
299 | @Override
300 | public void onPageScrollStateChanged(int state) {
301 |
302 | }
303 |
304 | private class MyOnClickListener implements OnClickListener {
305 |
306 | private int currentIndex;
307 |
308 | public MyOnClickListener(int i) {
309 | this.currentIndex = i;
310 | }
311 |
312 | @Override
313 | public void onClick(View v) {
314 | //点击时判断是否需要拦截跳转
315 | if (mOnPageChangeInterceptor != null
316 | && mOnPageChangeInterceptor.onIntercepted(currentIndex)){
317 | return;
318 | }
319 | if (currentIndex == mCurrentItem) {
320 | //如果还是同个页签,判断是否要回调
321 | if (onItemSelectedListener != null && mSameTabClickCallBack){
322 | onItemSelectedListener.onItemSelected(getBottomItem(currentIndex), mCurrentItem, currentIndex);
323 | }
324 | }else{
325 | if (mViewPager != null || mViewPager2 != null) {
326 | if (mViewPager != null){
327 | mViewPager.setCurrentItem(currentIndex, mSmoothScroll);
328 | }else {
329 | mViewPager2.setCurrentItem(currentIndex, mSmoothScroll);
330 | }
331 | return;
332 | }
333 | if (onItemSelectedListener != null){
334 | onItemSelectedListener.onItemSelected(getBottomItem(currentIndex), mCurrentItem, currentIndex);
335 | }
336 | updateTabState(currentIndex);
337 | }
338 | }
339 | }
340 |
341 | private void updateTabState(int position) {
342 | resetState();
343 | mCurrentItem = position;
344 | mItemViews.get(mCurrentItem).refreshTab(true);
345 | }
346 |
347 | /**
348 | * 重置当前按钮的状态
349 | */
350 | private void resetState() {
351 | if (mCurrentItem < mItemViews.size()) {
352 | if (mItemViews.get(mCurrentItem).isSelected()){
353 | mItemViews.get(mCurrentItem).refreshTab(false);
354 | }
355 | }
356 | }
357 |
358 | public void setCurrentItem(int currentItem) {
359 | if (mViewPager != null || mViewPager2 != null) {
360 | if (mViewPager != null){
361 | mViewPager.setCurrentItem(currentItem, mSmoothScroll);
362 | }else {
363 | mViewPager2.setCurrentItem(currentItem, mSmoothScroll);
364 | }
365 | } else {
366 | if (onItemSelectedListener != null) {
367 | onItemSelectedListener.onItemSelected(getBottomItem(currentItem), mCurrentItem, currentItem);
368 | }
369 | updateTabState(currentItem);
370 | }
371 | }
372 |
373 | /**
374 | * 设置未读数
375 | *
376 | * @param position 底部标签的下标
377 | * @param unreadNum 未读数
378 | */
379 | public void setUnread(int position, int unreadNum) {
380 | mItemViews.get(position).setUnreadNum(unreadNum);
381 | }
382 |
383 | /**
384 | * 设置提示消息
385 | *
386 | * @param position 底部标签的下标
387 | * @param msg 未读数
388 | */
389 | public void setMsg(int position, String msg) {
390 | mItemViews.get(position).setMsg(msg);
391 | }
392 |
393 | /**
394 | * 隐藏提示消息
395 | *
396 | * @param position 底部标签的下标
397 | */
398 | public void hideMsg(int position) {
399 | mItemViews.get(position).hideMsg();
400 | }
401 |
402 | /**
403 | * 显示提示的小红点
404 | *
405 | * @param position 底部标签的下标
406 | */
407 | public void showNotify(int position) {
408 | mItemViews.get(position).showNotify();
409 | }
410 |
411 | /**
412 | * 隐藏提示的小红点
413 | *
414 | * @param position 底部标签的下标
415 | */
416 | public void hideNotify(int position) {
417 | mItemViews.get(position).hideNotify();
418 | }
419 |
420 | public int getCurrentItem() {
421 | return mCurrentItem;
422 | }
423 |
424 | public void setSmoothScroll(boolean smoothScroll) {
425 | this.mSmoothScroll = smoothScroll;
426 | }
427 |
428 | public BottomBarItem getBottomItem(int position) {
429 | return mItemViews.get(position);
430 | }
431 |
432 | private OnItemSelectedListener onItemSelectedListener;
433 |
434 | public interface OnItemSelectedListener {
435 | void onItemSelected(BottomBarItem bottomBarItem, int previousPosition, int currentPosition);
436 | }
437 |
438 | public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
439 | this.onItemSelectedListener = onItemSelectedListener;
440 | }
441 |
442 | private OnPageChangeInterceptor mOnPageChangeInterceptor;
443 |
444 | public void setOnPageChangeInterceptor(OnPageChangeInterceptor onPageChangedInterceptor) {
445 | mOnPageChangeInterceptor = onPageChangedInterceptor;
446 | }
447 |
448 | public interface OnPageChangeInterceptor {
449 | boolean onIntercepted(int position);
450 | }
451 | }
452 |
--------------------------------------------------------------------------------
/library/src/main/java/com/chaychan/library/MyLottieAnimationView.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.content.Context;
4 | import android.os.Parcelable;
5 | import android.util.AttributeSet;
6 |
7 | import com.airbnb.lottie.LottieAnimationView;
8 |
9 | /**
10 | * @author ChayChan
11 | * @description: 去除LottieAnimationView的缓存
12 | * @date 2020/11/23 16:02
13 | */
14 | class MyLottieAnimationView extends LottieAnimationView {
15 |
16 | public MyLottieAnimationView(Context context) {
17 | super(context);
18 | }
19 |
20 | public MyLottieAnimationView(Context context, AttributeSet attrs) {
21 | super(context, attrs);
22 | }
23 |
24 | public MyLottieAnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | }
27 |
28 | /**
29 | * 重写此方法将LottieAnimationView的缓存去除
30 | * 解决因异常情况或旋转方向后页面重新加载
31 | * 导致lottie文件读取成最后一个tab文件的bug
32 | * @return
33 | */
34 | @Override
35 | protected Parcelable onSaveInstanceState() {
36 | Parcelable parcelable = super.onSaveInstanceState();
37 | parcelable = null;
38 | return null;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/library/src/main/java/com/chaychan/library/TabData.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | /**
6 | * @author chay
7 | * @description:
8 | * @date 2024/7/14 16:58
9 | */
10 | public class TabData {
11 | private String title;
12 |
13 | private int normalIconResId;
14 | private Drawable normalIcon;
15 |
16 | private int selectedIconResId;
17 | private Drawable selectedIcon;
18 |
19 | private String lottieJson;
20 |
21 | private int iconWidth;
22 | private int iconHeight;
23 |
24 | public TabData(String title, Drawable normalIcon, Drawable selectedIcon){
25 | this.title = title;
26 | this.normalIcon = normalIcon;
27 | this.selectedIcon = selectedIcon;
28 | }
29 |
30 | public TabData(String title, int normalIconResId, int selectedIconResId){
31 | this.title = title;
32 | this.normalIconResId = normalIconResId;
33 | this.selectedIconResId = selectedIconResId;
34 | }
35 |
36 | public TabData(String title, String lottieJson){
37 | this.title = title;
38 | this.lottieJson = lottieJson;
39 | }
40 |
41 | public String getLottieJson() {
42 | return lottieJson;
43 | }
44 |
45 | public void setLottieJson(String lottieJson) {
46 | this.lottieJson = lottieJson;
47 | }
48 |
49 | public int getIconWidth() {
50 | return iconWidth;
51 | }
52 |
53 | public void setIconWidth(int iconWidth) {
54 | this.iconWidth = iconWidth;
55 | }
56 |
57 | public int getIconHeight() {
58 | return iconHeight;
59 | }
60 |
61 | public void setIconHeight(int iconHeight) {
62 | this.iconHeight = iconHeight;
63 | }
64 |
65 | public int getNormalIconResId() {
66 | return normalIconResId;
67 | }
68 |
69 | public void setNormalIconResId(int normalIconResId) {
70 | this.normalIconResId = normalIconResId;
71 | }
72 |
73 | public int getSelectedIconResId() {
74 | return selectedIconResId;
75 | }
76 |
77 | public void setSelectedIconResId(int selectedIconResId) {
78 | this.selectedIconResId = selectedIconResId;
79 | }
80 |
81 | public String getTitle() {
82 | return title;
83 | }
84 |
85 | public void setTitle(String title) {
86 | this.title = title;
87 | }
88 |
89 | public Drawable getNormalIcon() {
90 | return normalIcon;
91 | }
92 |
93 | public void setNormalIcon(Drawable normalIcon) {
94 | this.normalIcon = normalIcon;
95 | }
96 |
97 | public Drawable getSelectedIcon() {
98 | return selectedIcon;
99 | }
100 |
101 | public void setSelectedIcon(Drawable selectedIcon) {
102 | this.selectedIcon = selectedIcon;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/library/src/main/java/com/chaychan/library/UIUtils.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 |
6 | /**
7 | * @author chaychan
8 | * @date 2017/3/7 17:19
9 | */
10 | public class UIUtils {
11 | /**
12 | * dip-->px
13 | */
14 | public static int dip2Px(Context context,int dip) {
15 | // px/dip = density;
16 | // density = dpi/160
17 | // 320*480 density = 1 1px = 1dp
18 | // 1280*720 density = 2 2px = 1dp
19 |
20 | float density = context.getResources().getDisplayMetrics().density;
21 | int px = (int) (dip * density + 0.5f);
22 | return px;
23 | }
24 |
25 | /**
26 | * 将sp值转换为px值,保证文字大小不变
27 | *
28 | * @param spValue
29 | * @return
30 | */
31 | public static int sp2px(Context context,float spValue) {
32 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
33 | return (int) (spValue * fontScale + 0.5f);
34 | }
35 |
36 | public static int getColor(Context context,int colorId){
37 | return context.getResources().getColor(colorId);
38 | }
39 |
40 | public static Drawable getDrawable(Context context,int resId){
41 | return context.getResources().getDrawable(resId);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/shape_msg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/shape_notify_point.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/shape_unread.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/item_bottom_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
13 |
14 |
20 |
21 |
27 |
28 |
41 |
42 |
54 |
55 |
65 |
66 |
67 |
68 |
69 |
74 |
75 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #999999
4 | #ff0000
5 |
6 | #ffffff
7 | #DDDDDD
8 | #F3F5F4
9 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | library
3 |
4 |
--------------------------------------------------------------------------------
/library/src/test/java/com/chaychan/library/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.chaychan.library;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':demo', ':library'
2 |
--------------------------------------------------------------------------------
/update-note-en.md:
--------------------------------------------------------------------------------
1 | ### V3.0.0 版本更新(2024-07-17)
2 | - Refactored BottomBarLayout, no need to specify item layout in xml, import tab by setting data source;
3 | - Support middle icon bulge, middle icon width and height setting, height from bottom
4 |
5 | ### V2.1.0 版本更新说明(2024-07-10)
6 |
7 | - Support ViewPager2
8 | - Add a pre-jump interceptor
9 |
10 | ### V2.0.0 update instructions (2020-11-21)
11 |
12 | - Migrate to AndroidX
13 | - Support to use lottie
14 |
15 | ### V1.1.2 update instructions (2018-12-13)
16 |
17 | - Support for dynamically adding and removing item
18 |
19 | ### V1.1.2 update instructions(2018-03-20)
20 |
21 | - Add attributes to modify unread font colors and background
22 |
23 | - Add attributes to modify prompt text font colors and background
24 |
25 | - Add attributes to modify prompt point background
26 |
27 |
28 | ### V1.1.1 update instructions(2018-02-27)
29 |
30 | - Repair must be set up ViewPager problems, can be modified to set or not set;
31 |
32 | - Modify the click callback, callback more than a previousPosition (last page location)
33 |
34 | - Add two usage patterns
35 |
36 |
37 | ### V1.0.7 update instructions(2018-01-05)
38 |
39 | - Increase the threshold for unreadable attributes and set the properties for smooth transitions;
40 |
41 | - Remove oriention restrictions。
42 |
43 | ### V1.0.6 update instructions(2017-12-19)
44 |
45 | - Add slide listener, callback onItemSelected ()
46 |
47 | ### V1.0.4 update instructions(2017-10-10)
48 |
49 | - Increase the number of unread, tips red dot, suggesting the function of the message
--------------------------------------------------------------------------------
/update-note.md:
--------------------------------------------------------------------------------
1 | ### V3.0.0 版本更新(2024-07-17)
2 | - 重构BottomBarLayout,xml中不需要指定item的布局,通过设置数据源导入tab;
3 | - 支持中间图标凸起、中间图标宽高设置、距离底部高度等
4 |
5 |
6 | ### V2.1.0 版本更新说明(2024-07-10)
7 |
8 | - 支持ViewPager2
9 | - 添加跳转前拦截监听
10 |
11 | ### V2.0.0版本更新说明 (2020-11-21)
12 |
13 | - 迁移至AndroidX
14 | - 支持lottie
15 |
16 | ### V1.2.0版本更新说明 (2018-12-13)
17 |
18 | - 支持动态添加、移除条目
19 |
20 | ### V1.1.2版本更新说明(2018-03-20)
21 |
22 | - 添加修改未读数字体颜色和背景的属性
23 |
24 | - 添加修改提示文字字体颜色和背景的属性
25 |
26 | - 添加修改提示点背景的属性
27 |
28 | ### V1.1.1版本更新说明(2018-02-27)
29 |
30 | - 修复一定要设置ViewPager的问题,修改成可设置或不设置;
31 |
32 | - 修改点击回调,回调多一个previousPosition(上个页签的位置)
33 |
34 | - 添加两种使用方式的demo演示
35 |
36 |
37 | ### V1.0.7版本更新说明(2018-01-05)
38 |
39 | - 增加未读数阈值属性和设置是否平滑切换的属性;
40 |
41 | - 去除oriention的限制。
42 |
43 | ### V1.0.6版本更新说明(2017-12-19)
44 |
45 | - 添加滑动监听,回调onItemSelected()
46 |
47 | ### V1.0.4版本更新说明(2017-10-10)
48 |
49 | - 增加未读数、提示小红点、提示消息的功能
--------------------------------------------------------------------------------