Repository: youth5201314/banner Branch: master Commit: 777c7dbfaa0a Files: 118 Total size: 219.4 KB Directory structure: gitextract_a7zjbpp5/ ├── .gitignore ├── BannerExample.iml ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── test/ │ │ └── banner/ │ │ ├── MainActivity.java │ │ ├── adapter/ │ │ │ ├── ImageAdapter.java │ │ │ ├── ImageNetAdapter.java │ │ │ ├── ImageTitleAdapter.java │ │ │ ├── ImageTitleNumAdapter.java │ │ │ ├── MultipleTypesAdapter.java │ │ │ ├── MyRecyclerViewAdapter.java │ │ │ └── TopLineAdapter.java │ │ ├── bean/ │ │ │ └── DataBean.java │ │ ├── indicator/ │ │ │ └── NumIndicator.java │ │ ├── ui/ │ │ │ ├── BannerFragment.java │ │ │ ├── BannerListFragment.java │ │ │ ├── BlankFragment.java │ │ │ ├── ConstraintLayoutBannerActivity.java │ │ │ ├── GalleryActivity.java │ │ │ ├── RecyclerViewBannerActivity.java │ │ │ ├── TVActivity.java │ │ │ ├── TouTiaoActivity.java │ │ │ ├── VideoActivity.java │ │ │ └── Vp2FragmentRecyclerviewActivity.java │ │ ├── util/ │ │ │ ├── ParentRecyclerView.java │ │ │ └── TabLayoutMediator.java │ │ └── viewholder/ │ │ ├── ImageHolder.java │ │ ├── ImageTitleHolder.java │ │ ├── TitleHolder.java │ │ └── VideoHolder.java │ └── res/ │ ├── drawable/ │ │ ├── background.xml │ │ ├── default_selecter.xml │ │ ├── green.xml │ │ ├── selected_radius.xml │ │ ├── unselected_radius.xml │ │ └── white.xml │ ├── layout/ │ │ ├── activity_constraint_layout_banner.xml │ │ ├── activity_gallery.xml │ │ ├── activity_main.xml │ │ ├── activity_recyclerview_banner.xml │ │ ├── activity_t_v.xml │ │ ├── activity_tou_tiao.xml │ │ ├── activity_video.xml │ │ ├── activity_vp2_fragment_recyclerview.xml │ │ ├── banner.xml │ │ ├── banner_image.xml │ │ ├── banner_image_title.xml │ │ ├── banner_image_title_num.xml │ │ ├── banner_title.xml │ │ ├── banner_video.xml │ │ ├── item.xml │ │ ├── test.xml │ │ └── top_line_item2.xml │ ├── values/ │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-w820dp/ │ └── dimens.xml ├── banner/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── youth/ │ │ └── banner/ │ │ ├── Banner.java │ │ ├── adapter/ │ │ │ ├── BannerAdapter.java │ │ │ └── BannerImageAdapter.java │ │ ├── config/ │ │ │ ├── BannerConfig.java │ │ │ └── IndicatorConfig.java │ │ ├── holder/ │ │ │ ├── BannerImageHolder.java │ │ │ └── IViewHolder.java │ │ ├── indicator/ │ │ │ ├── BaseIndicator.java │ │ │ ├── CircleIndicator.java │ │ │ ├── DrawableIndicator.java │ │ │ ├── Indicator.java │ │ │ ├── RectangleIndicator.java │ │ │ └── RoundLinesIndicator.java │ │ ├── itemdecoration/ │ │ │ └── MarginDecoration.java │ │ ├── listener/ │ │ │ ├── OnBannerListener.java │ │ │ └── OnPageChangeListener.java │ │ ├── transformer/ │ │ │ ├── AlphaPageTransformer.java │ │ │ ├── BasePageTransformer.java │ │ │ ├── DepthPageTransformer.java │ │ │ ├── MZScaleInTransformer.java │ │ │ ├── RotateDownPageTransformer.java │ │ │ ├── RotateUpPageTransformer.java │ │ │ ├── RotateYTransformer.java │ │ │ ├── ScaleInTransformer.java │ │ │ └── ZoomOutPageTransformer.java │ │ └── util/ │ │ ├── BannerLifecycleObserver.java │ │ ├── BannerLifecycleObserverAdapter.java │ │ ├── BannerUtils.java │ │ ├── LogUtils.java │ │ └── ScrollSpeedManger.java │ └── res/ │ └── values/ │ ├── attr.xml │ ├── ids.xml │ └── strings.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── update_message.md └── usekotlin/ ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src/ ├── androidTest/ │ └── java/ │ └── com/ │ └── spring/ │ └── usekotlin/ │ └── ExampleInstrumentedTest.kt ├── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── spring/ │ │ └── usekotlin/ │ │ ├── ImageAdapter.kt │ │ └── MainActivity.kt │ └── res/ │ ├── layout/ │ │ └── activity_main.xml │ └── values/ │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── test/ └── java/ └── com/ └── spring/ └── usekotlin/ └── ExampleUnitTest.kt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ .idea /banner.iml /gradlew /gradlew.bat /gradle.properties ================================================ FILE: BannerExample.iml ================================================ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ ## Banner 2.0 全新升级 > 只做一个可以自定义的轮播容器,不侵入UI ———— Banner 2.0 ``` > 各位老铁反馈的问题我都有看,不是不解决,有时真的很难复现, > 如果能在提交问题时,有条件提供个demo发到我邮箱,方便定位问题,毕竟大家的场景和使用方式都有差异, > 我自己也在使用,在多个千万级APP上一直稳定运行,如果真有严重问题肯定第一时间就修复了 ``` Banner 1.4.10(还想看老版本的可以点击这里) ### 阔别已久,从新回归 * 首先我声明几点: * 这只是一个开源库,如果满意你可以使用、可以借鉴修改,希望对你们有所帮助。 * 如果不满意请友好的提出,注明错误的详细信息或者修改建议,好的想法和自定义的东西亦可以直接提交,大家都能来一起完善。 * 如果你觉得实在是没用,也请你做一个有自我修养的人。 ### 主要改进功能介绍 最开始是想上传以前基于viewpager更新好的版本,但是看着viewpager2正式版已经出来了,就上新的吧,viewpager2确实比viewpager性能好很多。 - [x] 使用了ViewPager2为基础控件 ViewPager2介绍 - [x] 支持了androidx兼容包 - [x] 方便了UI、Indicator自定义 - [x] 支持画廊效果、魅族效果 - [x] 兼容了水平和垂直轮播,也可以实现类似淘宝头条的效果 - [x] 依赖包目前只需要导入了ViewPager2 ### 效果图 更多效果运行demo查看 ![默认](images/banner_example.gif) ![画廊](images/banner_example2.gif) ![魅族](images/banner_example1.gif) ![头条](images/banner_example3.gif) ##### 内置了多种PageTransformer效果 ![DepthPageTransformer](images/DepthPageTransformer.gif) ![ZoomOutPageTransformer](images/ZoomOutPageTransformer.gif) |内置的PageTransformer| |---| |AlphaPageTransformer| |DepthPageTransformer| |RotateDownPageTransformer| |RotateUpPageTransformer| |RotateYTransformer| |ScaleInTransformer| |ZoomOutPageTransformer| 也可以组合使用效果更佳 ## 方法 更多方法以实际使用为准,下面不一定全部列出了 |方法名|返回类型|描述| |---|---|---| |getAdapter()|extends BannerAdapter|获取你设置的BannerAdapter |getViewPager2()|ViewPager2|获取ViewPager2 |getIndicator()|Indicator|获取你设置的指示器(没有设置直接获取会抛异常哦) |getIndicatorConfig()|IndicatorConfig|获取你设置的指示器配置信息(没有设置直接获取会抛异常哦) |getRealCount()|int|返回banner真实总数 |setUserInputEnabled(boolean)|this|禁止手动滑动Banner;true 允许,false 禁止 |setDatas(List)|this|重新设置banner数据 |isAutoLoop(boolean)|this|是否允许自动轮播 |setLoopTime(long)|this|设置轮播间隔时间(默认3000毫秒) |setScrollTime(long)|this|设置轮播滑动的时间(默认800毫秒) |start()|this|开始轮播(主要配合生命周期使用),或者你手动暂停再次启动 |stop()|this|停止轮播(主要配合生命周期使用),或者你需要手动暂停 |setAdapter(T extends BannerAdapter)|this|设置banner的适配器 |setAdapter(T extends BannerAdapter,boolean)|this|设置banner的适配器,是否支持无限循环 |setOrientation(@Orientation)|this|设置banner轮播方向(垂直or水平) |setOnBannerListener(this)|this|设置点击事件,下标是从0开始 |addOnPageChangeListener(this)|this|添加viewpager2的滑动监听 |setPageTransformer(PageTransformer)|this|设置viewpager的切换效果 |addPageTransformer(PageTransformer)|this|添加viewpager的切换效果(可以设置多个) |setIndicator(Indicator)|this|设置banner轮播指示器(提供有base和接口,可以自定义) |setIndicator(Indicator,boolean)|this|设置指示器(传false代表不将指示器添加到banner上,配合布局文件,可以自我发挥) |setIndicatorSelectedColor(@ColorInt)|this|设置指示器选中颜色 |setIndicatorSelectedColorRes(@ColorRes)|this|设置指示器选中颜色 |setIndicatorNormalColor(@ColorInt)|this|设置指示器默认颜色 |setIndicatorNormalColorRes(@ColorRes)|this|设置指示器默认颜色 |setIndicatorGravity(@IndicatorConfig.Direction)|this|设置指示器位置(左,中,右) |setIndicatorSpace(int)|this|设置指示器之间的间距 |setIndicatorMargins(IndicatorConfig.Margins)|this|设置指示器的Margins |setIndicatorWidth(int,int)|this|设置指示器选中和未选中的宽度,直接影响绘制指示器的大小 |setIndicatorNormalWidth(int)|this|设置指示器未选中的宽度 |setIndicatorSelectedWidth(int)|this|设置指示器选中的宽度 |setIndicatorRadius(int)|this|设置指示器圆角,不要圆角可以设置为0 |setIndicatorHeight(int)|this|设置指示器高度 |setBannerRound(float)|this|设置banner圆角(还有一种setBannerRound2方法,需要5.0以上) |setBannerGalleryEffect(int,int,float)|this|画廊效果 |setBannerGalleryMZ(int,float)|this|魅族效果 |setStartPosition(int)|this|设置开始的位置 (需要在setAdapter或者setDatas之前调用才有效哦) |setIndicatorPageChange()|this|设置指示器改变监听 (一般是为了配合数据操作使用,看情况自己发挥) |setCurrentItem()|this|设置当前位置,和原生使用效果一样 |addBannerLifecycleObserver()|this|给banner添加生命周期观察者,内部自动管理banner的生命周期 ## Attributes属性 >在banner布局文件中调用,如果你自定义了indicator请做好兼容处理。 下面的属性并不是每个指示器都用得到,所以使用时要注意! |Attributes|format|describe |---|---|---| |banner_loop_time|integer|轮播间隔时间,默认3000 |banner_auto_loop|boolean|是否自动轮播,默认true |banner_infinite_loop|boolean|是否支持无限循环(即首尾直接过渡),默认true |banner_orientation|enum|轮播方向:horizontal(默认) or vertical |banner_radius|dimension|banner圆角半径,默认0(不绘制圆角) |banner_indicator_normal_width|dimension|指示器默认的宽度,默认5dp (对RoundLinesIndicator无效) |banner_indicator_selected_width|dimension|指示器选中的宽度,默认7dp |banner_indicator_normal_color|color|指示器默认颜色,默认0x88ffffff |banner_indicator_selected_color|color|指示器选中颜色,默认0x88000000 |banner_indicator_space|dimension|指示器之间的间距,默认5dp (对RoundLinesIndicator无效) |banner_indicator_gravity|dimension|指示器位置,默认center |banner_indicator_margin|dimension|指示器的margin,默认5dp,不能和下面的同时使用 |banner_indicator_marginLeft|dimension|指示器左边的margin |banner_indicator_marginTop|dimension|指示器上边的margin |banner_indicator_marginRight|dimension|指示器右边的margin |banner_indicator_marginBottom|dimension|指示器下边的margin |banner_indicator_height|dimension|指示器高度(对CircleIndicator无效) |banner_indicator_radius|dimension|指示器圆角(对CircleIndicator无效) |banner_round_top_left|boolean|设置要绘制的banner圆角方向(如果都不设置默认全部) |banner_round_top_right|boolean|设置要绘制的banner圆角方向(如果都不设置默认全部) |banner_round_bottom_left|boolean|设置要绘制的banner圆角方向(如果都不设置默认全部) |banner_round_bottom_right|boolean|设置要绘制的banner圆角方向(如果都不设置默认全部) ## 使用步骤 >以下提供的是最简单的步骤,需要复杂的样式自己可以自定义 #### Step 1.依赖banner Gradle ```groovy repositories { maven { url "https://s01.oss.sonatype.org/content/groups/public" } } dependencies{ //2.1.0以前jcenter的依赖 //implementation 'com.youth.banner:banner:2.1.0' //现在Maven Central implementation 'io.github.youth5201314:banner:2.2.3' } ``` #### Step 2.添加权限到你的 AndroidManifest.xml ```xml ``` #### Step 3.在布局文件中添加Banner,可以设置自定义属性 !!!此步骤可以省略,可以直接在Activity或者Fragment中new Banner(); ```xml ``` #### Step 4.继承BannerAdapter,和RecyclerView的Adapter一样(如果你只是图片轮播也可以使用默认的) !!!此步骤可以省略,图片轮播提供有默认适配器,其他的没有提供是因为大家的可变性要求不确定,所以直接自定义的比较好。 ```java /** * 自定义布局,下面是常见的图片样式,更多实现可以看demo,可以自己随意发挥 */ public class ImageAdapter extends BannerAdapter { public ImageAdapter(List mDatas) { //设置数据,也可以调用banner提供的方法,或者自己在adapter中实现 super(mDatas); } //创建ViewHolder,可以用viewType这个字段来区分不同的ViewHolder @Override public BannerViewHolder onCreateHolder(ViewGroup parent, int viewType) { ImageView imageView = new ImageView(parent.getContext()); //注意,必须设置为match_parent,这个是viewpager2强制要求的 imageView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return new BannerViewHolder(imageView); } @Override public void onBindView(BannerViewHolder holder, DataBean data, int position, int size) { holder.imageView.setImageResource(data.imageRes); } class BannerViewHolder extends RecyclerView.ViewHolder { ImageView imageView; public BannerViewHolder(@NonNull ImageView view) { super(view); this.imageView = view; } } } ``` #### Step 5.Banner具体方法调用 ```java public class BannerActivity extends AppCompatActivity { public void useBanner() { //--------------------------简单使用------------------------------- banner.addBannerLifecycleObserver(this)//添加生命周期观察者 .setAdapter(new BannerExampleAdapter(DataBean.getTestData())) .setIndicator(new CircleIndicator(this)); //—————————————————————————如果你想偷懒,而又只是图片轮播———————————————————————— banner.setAdapter(new BannerImageAdapter(DataBean.getTestData3()) { @Override public void onBindView(BannerImageHolder holder, DataBean data, int position, int size) { //图片加载自己实现 Glide.with(holder.itemView) .load(data.imageUrl) .apply(RequestOptions.bitmapTransform(new RoundedCorners(30))) .into(holder.imageView); } }) .addBannerLifecycleObserver(this)//添加生命周期观察者 .setIndicator(new CircleIndicator(this)); //更多使用方法仔细阅读文档,或者查看demo } } ``` ## Banner使用中优化体验 **如果你需要考虑更好的体验,可以看看下面的代码** #### Step 1.(可选)生命周期改变时 ```java public class BannerActivity { //方法一:自己控制banner的生命周期 @Override protected void onStart() { super.onStart(); //开始轮播 banner.start(); } @Override protected void onStop() { super.onStop(); //停止轮播 banner.stop(); } @Override protected void onDestroy() { super.onDestroy(); //销毁 banner.destroy(); } //方法二:调用banner的addBannerLifecycleObserver()方法,让banner自己控制 protected void onCreate(Bundle savedInstanceState) { //添加生命周期观察者 banner.addBannerLifecycleObserver(this); } } ``` ## 常见问题(收录被反复询问的问题) * 网络图片加载不出来? `banner本身不提供图片加载功能,首先确认banner本身使用是否正确,具体参考demo, 然后请检查你的图片加载框架或者网络请求框架,服务端也可能加了https安全认证,是看下是否报有证书相关错误` * 怎么实现视频轮播? `demo中有实现类似淘宝商品详情的效果,第一个放视频,后面的放的是图片,并且可以设置首尾不能滑动。 因为大家使用的播放器不一样业务环境也不同,具体情况自己把握,demo就是给一个思路哈!可以参考和修改` * 我想指定轮播开始的位置? `现在提供了setStartPosition()方法,在sheAdapter和setDatas直接调用一次就行了,当然setAdapter后通过setCurrentItem设置也行` * 父控件滑动时,banner切换会获取焦点,然后自动全部显示。不想让banner获取焦点可以给父控件加上: ``` //banner也一定要用最新版哦! android:focusable="true" android:focusableInTouchMode="true" ``` * 怎么设置圆角? 1、调用提供的方法或者自定义属性进行设置,这里设置的是banner本身的圆角,不是轮播内view的圆角 2、在adapter中对自定义的view进行自己实现,就拿图片举例:可以自己定义一个圆角的ImageView控件,或者使用glide渲染都行。请举一反三,view都自定义了还有什么不能改的? ## Thanks - [MZBannerView](https://github.com/pinguo-zhouwei/MZBannerView) - [MagicViewPager](https://github.com/hongyangAndroid/MagicViewPager) - [zguop的viewpager2的滑动时间解决方案](https://github.com/zguop/banner/blob/master/pager2banner/src/main/java/com/to/aboomy/pager2banner/Banner.java) ### 联系方式 * 我的个人微博:https://weibo.com/u/3013494003 有兴趣的也可以关注,大家一起交流 * 有问题可以加群大家一起交流,如果你觉得对你有帮助可以扫描下面支付宝二维码随意打赏下哦! ## 更新说明 [更新说明](update_message.md) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdk 33 defaultConfig { applicationId "com.test.banner" minSdk 21 targetSdk 33 versionCode 1 versionName "1.0" consumerProguardFiles 'proguard-rules.pro' } buildTypes { debug { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.2.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01' implementation 'androidx.viewpager:viewpager:1.0.0' implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.2' implementation 'androidx.cardview:cardview:1.0.0' implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' implementation 'com.google.android.material:material:1.2.0-alpha06' implementation 'com.jakewharton:butterknife:10.2.3' annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3' implementation 'com.shuyu:GSYVideoPlayer:7.1.3' implementation project(':banner') // implementation 'io.github.youth5201314:banner:2.2.1' } ================================================ FILE: app/proguard-rules.pro ================================================ #指定代码的压缩级别 -optimizationpasses 5 #包明不混合大小写 -dontusemixedcaseclassnames #不去忽略非公共的库类 -dontskipnonpubliclibraryclasses #优化 不优化输入的类文件 -dontoptimize #预校验 -dontpreverify #混淆时是否记录日志 -verbose # 混淆时所采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #保护注解 -keepattributes *Annotation* # 保持哪些类不被混淆 -keep public class * extends android.app.Fragment -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService #glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * extends com.bumptech.glide.module.AppGlideModule -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { **[] $VALUES; public *; } #butterknife -keep public class * implements butterknife.Unbinder { public (**, android.view.View); } -keep class butterknife.* -keepclasseswithmembernames class * { @butterknife.* ; } -keepclasseswithmembernames class * { @butterknife.* ; } -keepclassmembers class * { @butterknife.* ; } -keepclassmembers class * { @butterknife.* ; } #gsyvideoplayer -keep class com.shuyu.gsyvideoplayer.video.** { *; } -dontwarn com.shuyu.gsyvideoplayer.video.** -keep class com.shuyu.gsyvideoplayer.video.base.** { *; } -dontwarn com.shuyu.gsyvideoplayer.video.base.** -keep class com.shuyu.gsyvideoplayer.utils.** { *; } -dontwarn com.shuyu.gsyvideoplayer.utils.** -keep class tv.danmaku.ijk.** { *; } -dontwarn tv.danmaku.ijk.** -keep public class * extends android.view.View{ *** get*(); void set*(***); public (android.content.Context); public (android.content.Context, android.util.AttributeSet); public (android.content.Context, android.util.AttributeSet, int); } -dontwarn com.google.android.exoplayer.** -keep class com.google.android.exoplayer.**{*;} ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/test/banner/MainActivity.java ================================================ package com.test.banner; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.view.View; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; import com.google.android.material.snackbar.Snackbar; import com.test.banner.adapter.ImageAdapter; import com.test.banner.adapter.ImageTitleAdapter; import com.test.banner.adapter.ImageTitleNumAdapter; import com.test.banner.adapter.MultipleTypesAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.indicator.DrawableIndicator; import com.test.banner.ui.ConstraintLayoutBannerActivity; import com.test.banner.ui.GalleryActivity; import com.test.banner.ui.RecyclerViewBannerActivity; import com.test.banner.ui.TVActivity; import com.test.banner.ui.TouTiaoActivity; import com.test.banner.ui.VideoActivity; import com.test.banner.ui.Vp2FragmentRecyclerviewActivity; import com.youth.banner.Banner; import com.youth.banner.adapter.BannerImageAdapter; import com.youth.banner.holder.BannerImageHolder; import com.youth.banner.config.BannerConfig; import com.youth.banner.config.IndicatorConfig; import com.youth.banner.indicator.CircleIndicator; import com.youth.banner.indicator.RoundLinesIndicator; import com.youth.banner.listener.OnPageChangeListener; import com.youth.banner.util.BannerUtils; import com.youth.banner.util.LogUtils; import androidx.appcompat.app.AppCompatActivity; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class MainActivity extends AppCompatActivity { @BindView(R.id.banner) Banner banner; @BindView(R.id.indicator) RoundLinesIndicator indicator; @BindView(R.id.swipeRefresh) SwipeRefreshLayout refresh; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); List datas = DataBean.getTestData2(); //自定义的图片适配器,也可以使用默认的BannerImageAdapter ImageAdapter adapter = new ImageAdapter(datas); banner.setAdapter(adapter) // .setCurrentItem(0,false) //添加生命周期观察者 .addBannerLifecycleObserver(this) //设置指示器 .setIndicator(new CircleIndicator(this)) .setOnBannerListener((data, position) -> { Snackbar.make(banner, ((DataBean) data).title, Snackbar.LENGTH_SHORT).show(); LogUtils.d("position:" + position); }); //添加item之间切换时的间距(如果使用了画廊效果就不要添加间距了,因为内部已经添加过了) // banner.addPageTransformer(new MarginPageTransformer( BannerUtils.dp2px(10))); //和下拉刷新配套使用 refresh.setOnRefreshListener(() -> { //模拟网络请求需要3秒,请求完成,设置setRefreshing 为false new Handler().postDelayed(() -> { refresh.setRefreshing(false); //给banner重新设置数据(完全覆盖) banner.setDatas(DataBean.getTestData()); //模拟请求成功(原数据减少) 刷新banner // datas.remove(0); // adapter.notifyDataSetChanged(); //对setDatas()方法不满意?你可以自己在adapter控制数据,参考setDatas()的实现修改 // adapter.updateData(DataBean.getTestData()); // banner.setCurrentItem(banner.getStartPosition(), false); // banner.setIndicatorPageChange(); }, 2000); }); } @OnClick({R.id.style_image, R.id.style_image_title, R.id.style_image_title_num, R.id.style_multiple, R.id.style_net_image, R.id.change_indicator, R.id.rv_banner, R.id.cl_banner, R.id.vp_banner, R.id.banner_video, R.id.banner_tv, R.id.gallery, R.id.topLine}) public void click(View view) { indicator.setVisibility(View.GONE); switch (view.getId()) { case R.id.style_image: refresh.setEnabled(true); banner.setAdapter(new ImageAdapter(DataBean.getTestData())); banner.setIndicator(new CircleIndicator(this)); banner.setIndicatorGravity(IndicatorConfig.Direction.CENTER); break; case R.id.style_image_title: refresh.setEnabled(true); banner.setAdapter(new ImageTitleAdapter(DataBean.getTestData())); banner.setIndicator(new CircleIndicator(this)); banner.setIndicatorGravity(IndicatorConfig.Direction.RIGHT); banner.setIndicatorMargins(new IndicatorConfig.Margins(0, 0, BannerConfig.INDICATOR_MARGIN, BannerUtils.dp2px(12))); break; case R.id.style_image_title_num: refresh.setEnabled(true); //这里是将数字指示器和title都放在adapter中的,如果不想这样你也可以直接设置自定义的数字指示器 banner.setAdapter(new ImageTitleNumAdapter(DataBean.getTestData())); banner.removeIndicator(); break; case R.id.style_multiple: refresh.setEnabled(true); banner.setIndicator(new CircleIndicator(this)); banner.setAdapter(new MultipleTypesAdapter(this, DataBean.getTestData())); break; case R.id.style_net_image: refresh.setEnabled(false); //方法一:使用自定义图片适配器 // banner.setAdapter(new ImageNetAdapter(DataBean.getTestData3())); //方法二:使用自带的图片适配器 banner.setAdapter(new BannerImageAdapter(DataBean.getTestData3()) { @Override public void onBindView(BannerImageHolder holder, DataBean data, int position, int size) { //图片加载自己实现 Glide.with(holder.itemView) .load(data.imageUrl) .thumbnail(Glide.with(holder.itemView).load(R.drawable.loading)) .apply(RequestOptions.bitmapTransform(new RoundedCorners(30))) .into(holder.imageView); } }); banner.setIndicator(new RoundLinesIndicator(this)); banner.setIndicatorSelectedWidth(BannerUtils.dp2px(15)); break; case R.id.change_indicator: indicator.setVisibility(View.VISIBLE); //在布局文件中使用指示器,这样更灵活 banner.setIndicator(indicator, false); banner.setIndicatorSelectedWidth(BannerUtils.dp2px(15)); break; case R.id.gallery: startActivity(new Intent(this, GalleryActivity.class)); break; case R.id.rv_banner: startActivity(new Intent(this, RecyclerViewBannerActivity.class)); break; case R.id.cl_banner: startActivity(new Intent(this, ConstraintLayoutBannerActivity.class)); break; case R.id.vp_banner: startActivity(new Intent(this, Vp2FragmentRecyclerviewActivity.class)); break; case R.id.banner_video: startActivity(new Intent(this, VideoActivity.class)); break; case R.id.banner_tv: startActivity(new Intent(this, TVActivity.class)); break; case R.id.topLine: startActivity(new Intent(this, TouTiaoActivity.class)); break; default: throw new IllegalStateException("Unexpected value: " + view.getId()); } } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/ImageAdapter.java ================================================ package com.test.banner.adapter; import android.view.ViewGroup; import android.widget.ImageView; import com.test.banner.bean.DataBean; import com.test.banner.viewholder.ImageHolder; import com.youth.banner.adapter.BannerAdapter; import java.util.List; /** * 自定义布局,图片 */ public class ImageAdapter extends BannerAdapter { public ImageAdapter(List mDatas) { //设置数据,也可以调用banner提供的方法,或者自己在adapter中实现 super(mDatas); } //更新数据 public void updateData(List data) { //这里的代码自己发挥,比如如下的写法等等 mDatas.clear(); mDatas.addAll(data); notifyDataSetChanged(); } //创建ViewHolder,可以用viewType这个字段来区分不同的ViewHolder @Override public ImageHolder onCreateHolder(ViewGroup parent, int viewType) { ImageView imageView = new ImageView(parent.getContext()); //注意,必须设置为match_parent,这个是viewpager2强制要求的 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); imageView.setLayoutParams(params); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); return new ImageHolder(imageView); } @Override public void onBindView(ImageHolder holder, DataBean data, int position, int size) { holder.imageView.setImageResource(data.imageRes); } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/ImageNetAdapter.java ================================================ package com.test.banner.adapter; import android.graphics.Bitmap; import android.graphics.Outline; import android.graphics.drawable.Drawable; import android.os.Build; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.BitmapImageViewTarget; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; import com.test.banner.R; import com.test.banner.bean.DataBean; import com.test.banner.viewholder.ImageHolder; import com.youth.banner.adapter.BannerAdapter; import com.youth.banner.util.BannerUtils; import java.util.List; /** * 自定义布局,网络图片 */ public class ImageNetAdapter extends BannerAdapter { public ImageNetAdapter(List mDatas) { super(mDatas); } @Override public ImageHolder onCreateHolder(ViewGroup parent, int viewType) { ImageView imageView = (ImageView) BannerUtils.getView(parent, R.layout.banner_image); //通过裁剪实现圆角 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { BannerUtils.setBannerRound(imageView, 20); } return new ImageHolder(imageView); } @Override public void onBindView(ImageHolder holder, DataBean data, int position, int size) { //通过图片加载器实现圆角,你也可以自己使用圆角的imageview,实现圆角的方法很多,自己尝试哈 Glide.with(holder.itemView) .load(data.imageUrl) .thumbnail(Glide.with(holder.itemView).load(R.drawable.loading)) .skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE) // .apply(RequestOptions.bitmapTransform(new RoundedCorners(30))) .into(holder.imageView); } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/ImageTitleAdapter.java ================================================ package com.test.banner.adapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.test.banner.bean.DataBean; import com.test.banner.R; import com.test.banner.viewholder.ImageTitleHolder; import com.youth.banner.adapter.BannerAdapter; import com.youth.banner.util.BannerUtils; import java.util.List; /** * 自定义布局,图片+标题 */ public class ImageTitleAdapter extends BannerAdapter { public ImageTitleAdapter(List mDatas) { super(mDatas); } @Override public ImageTitleHolder onCreateHolder(ViewGroup parent, int viewType) { return new ImageTitleHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.banner_image_title, parent, false)); } @Override public void onBindView(ImageTitleHolder holder, DataBean data, int position, int size) { holder.imageView.setImageResource(data.imageRes); holder.title.setText(data.title); } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/ImageTitleNumAdapter.java ================================================ package com.test.banner.adapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.bean.DataBean; import com.test.banner.R; import com.youth.banner.adapter.BannerAdapter; import java.util.List; /** * 自定义布局,图片+标题+数字指示器 */ public class ImageTitleNumAdapter extends BannerAdapter { public ImageTitleNumAdapter(List mDatas) { //设置数据,也可以调用banner提供的方法 super(mDatas); } //创建ViewHolder,可以用viewType这个字段来区分不同的ViewHolder @Override public BannerViewHolder onCreateHolder(ViewGroup parent, int viewType) { //注意布局文件,item布局文件要设置为match_parent,这个是viewpager2强制要求的 //或者调用BannerUtils.getView(parent,R.layout.banner_image_title_num); View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.banner_image_title_num, parent, false); return new BannerViewHolder(view); } //绑定数据 @Override public void onBindView(BannerViewHolder holder, DataBean data, int position, int size) { holder.imageView.setImageResource(data.imageRes); holder.title.setText(data.title); //可以在布局文件中自己实现指示器,亦可以使用banner提供的方法自定义指示器,目前样式较少,后面补充 holder.numIndicator.setText((position + 1) + "/" + size); } class BannerViewHolder extends RecyclerView.ViewHolder { ImageView imageView; TextView title; TextView numIndicator; public BannerViewHolder(@NonNull View view) { super(view); imageView = view.findViewById(R.id.image); title = view.findViewById(R.id.bannerTitle); numIndicator = view.findViewById(R.id.numIndicator); } } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/MultipleTypesAdapter.java ================================================ package com.test.banner.adapter; import android.content.Context; import android.graphics.Color; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; import com.test.banner.bean.DataBean; import com.test.banner.viewholder.ImageHolder; import com.test.banner.viewholder.TitleHolder; import com.test.banner.viewholder.VideoHolder; import com.youth.banner.adapter.BannerAdapter; import com.youth.banner.util.BannerUtils; import java.util.List; /** * 自定义布局,多个不同UI切换 */ public class MultipleTypesAdapter extends BannerAdapter { private Context context; private SparseArray mVHMap = new SparseArray<>(); public MultipleTypesAdapter(Context context, List mDatas) { super(mDatas); this.context = context; } @Override public RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType) { switch (viewType) { case 1: return new ImageHolder(BannerUtils.getView(parent, R.layout.banner_image)); case 2: return new VideoHolder(BannerUtils.getView(parent, R.layout.banner_video)); case 3: return new TitleHolder(BannerUtils.getView(parent, R.layout.banner_title)); } return new ImageHolder(BannerUtils.getView(parent, R.layout.banner_image)); } @Override public int getItemViewType(int position) { //先取得真实的position,在获取实体 // return getData(getRealPosition(position)).viewType; //直接获取真实的实体 return getRealData(position).viewType; //或者自己直接去操作集合 // return mDatas.get(getRealPosition(position)).viewType; } @Override public void onBindView(RecyclerView.ViewHolder holder, DataBean data, int position, int size) { int viewType = holder.getItemViewType(); switch (viewType) { case 1: ImageHolder imageHolder = (ImageHolder) holder; mVHMap.append(position,imageHolder); imageHolder.imageView.setImageResource(data.imageRes); break; case 2: VideoHolder videoHolder = (VideoHolder) holder; mVHMap.append(position,videoHolder); videoHolder.player.setUp(data.imageUrl, true, null); videoHolder.player.getBackButton().setVisibility(View.GONE); //增加封面 ImageView imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setImageResource(R.drawable.image4); videoHolder.player.setThumbImageView(imageView); // videoHolder.player.startPlayLogic(); break; case 3: TitleHolder titleHolder = (TitleHolder) holder; mVHMap.append(position,titleHolder); titleHolder.title.setText(data.title); titleHolder.title.setBackgroundColor(Color.parseColor(DataBean.getRandColor())); break; } } public SparseArray getVHMap() { return mVHMap; } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/MyRecyclerViewAdapter.java ================================================ package com.test.banner.adapter; import android.content.Context; import android.graphics.Color; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.indicator.RoundLinesIndicator; import com.youth.banner.util.BannerUtils; public class MyRecyclerViewAdapter extends RecyclerView.Adapter { private Context context; public MyRecyclerViewAdapter(Context context) { this.context = context; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType==R.layout.item) { return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false)); }else{ return new MyBannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.banner, parent, false)); } } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof MyViewHolder) { ((MyViewHolder) holder).cardView.setBackgroundColor(Color.parseColor(DataBean.getRandColor())); }else if (holder instanceof MyBannerViewHolder){ Banner banner=((MyBannerViewHolder) holder).banner; banner.setAdapter(new ImageNetAdapter(DataBean.getTestData3())); banner.setBannerRound(BannerUtils.dp2px(5)); banner.setIndicator(new RoundLinesIndicator(context)); banner.setIndicatorSelectedWidth((int) BannerUtils.dp2px(15)); } } @Override public int getItemViewType(int position) { if (position%2==0){ return R.layout.item; }else{ return R.layout.banner; } } @Override public int getItemCount() { return 10; } //banner 内部已实现 // @Override // public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { // super.onViewDetachedFromWindow(holder); // Log.e("banner_log", "onViewDetachedFromWindow:" + holder.getAdapterPosition()); // //定位你的位置 // if (holder.getAdapterPosition()%2!=0) { // if (holder instanceof MyBannerViewHolder) { // ((MyBannerViewHolder) holder).banner.stop(); // } // } // } // // @Override // public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { // super.onViewAttachedToWindow(holder); // Log.e("banner_log", "onViewAttachedToWindow:" + holder.getAdapterPosition()); // if (holder.getAdapterPosition()%2!=0) { // if (holder instanceof MyBannerViewHolder) { // ((MyBannerViewHolder) holder).banner.start(); // } // } // } class MyViewHolder extends RecyclerView.ViewHolder { public CardView cardView; public MyViewHolder(@NonNull View itemView) { super(itemView); cardView = itemView.findViewById(R.id.card_view); } } class MyBannerViewHolder extends RecyclerView.ViewHolder { public Banner banner; public MyBannerViewHolder(@NonNull View itemView) { super(itemView); banner = itemView.findViewById(R.id.banner); } } } ================================================ FILE: app/src/main/java/com/test/banner/adapter/TopLineAdapter.java ================================================ package com.test.banner.adapter; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.bean.DataBean; import com.test.banner.R; import com.youth.banner.adapter.BannerAdapter; import com.youth.banner.util.BannerUtils; import java.util.List; /** * 自定义布局,实现类似1号店、淘宝头条的滚动效果 */ public class TopLineAdapter extends BannerAdapter { public TopLineAdapter(List mDatas) { super(mDatas); } @Override public TopLineHolder onCreateHolder(ViewGroup parent, int viewType) { return new TopLineHolder(BannerUtils.getView(parent,R.layout.top_line_item2)); } @Override public void onBindView(TopLineHolder holder, DataBean data, int position, int size) { holder.message.setText(data.title); if (data.viewType==1) { holder.source.setText("1号店"); }else if (data.viewType==2) { holder.source.setText("淘宝头条"); }else if (data.viewType==3) { holder.source.setText("京东快报"); } } class TopLineHolder extends RecyclerView.ViewHolder { public TextView message; public TextView source; public TopLineHolder(@NonNull View view) { super(view); message=view.findViewById(R.id.message); source=view.findViewById(R.id.source); } } } ================================================ FILE: app/src/main/java/com/test/banner/bean/DataBean.java ================================================ package com.test.banner.bean; import com.test.banner.R; import java.util.ArrayList; import java.util.List; import java.util.Random; public class DataBean { public Integer imageRes; public String imageUrl; public String title; public int viewType; public DataBean(Integer imageRes, String title, int viewType) { this.imageRes = imageRes; this.title = title; this.viewType = viewType; } public DataBean(String imageUrl, String title, int viewType) { this.imageUrl = imageUrl; this.title = title; this.viewType = viewType; } public static List getTestData() { List list = new ArrayList<>(); list.add(new DataBean(R.drawable.image1, "相信自己,你努力的样子真的很美", 1)); list.add(new DataBean(R.drawable.image2, "极致简约,梦幻小屋", 3)); list.add(new DataBean(R.drawable.image3, "超级卖梦人", 3)); list.add(new DataBean(R.drawable.image4, "夏季新搭配", 1)); list.add(new DataBean(R.drawable.image5, "绝美风格搭配", 1)); list.add(new DataBean(R.drawable.image6, "微微一笑 很倾城", 3)); return list; } public static List getTestData2() { List list = new ArrayList<>(); list.add(new DataBean(R.drawable.image7, "听风.赏雨", 3)); list.add(new DataBean(R.drawable.image8, "迪丽热巴.迪力木拉提", 1)); list.add(new DataBean(R.drawable.image9, "爱美.人间有之", 3)); list.add(new DataBean(R.drawable.image10, "洋洋洋.气质篇", 1)); list.add(new DataBean(R.drawable.image11, "生活的态度", 3)); return list; } /** * 仿淘宝商品详情第一个是视频 * @return */ public static List getTestDataVideo() { List list = new ArrayList<>(); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/09/mp4/190309153658147087.mp4", "第一个放视频", 2)); list.add(new DataBean(R.drawable.image7, "听风.赏雨", 1)); list.add(new DataBean(R.drawable.image8, "迪丽热巴.迪力木拉提", 1)); list.add(new DataBean(R.drawable.image9, "爱美.人间有之", 1)); list.add(new DataBean(R.drawable.image10, "洋洋洋.气质篇", 1)); list.add(new DataBean(R.drawable.image11, "生活的态度", 1)); return list; } public static List getTestData3() { List list = new ArrayList<>(); list.add(new DataBean("https://img.zcool.cn/community/013de756fb63036ac7257948747896.jpg", null, 1)); list.add(new DataBean("https://img.zcool.cn/community/01639a56fb62ff6ac725794891960d.jpg", null, 1)); list.add(new DataBean("https://img.zcool.cn/community/01270156fb62fd6ac72579485aa893.jpg", null, 1)); list.add(new DataBean("https://img.zcool.cn/community/01233056fb62fe32f875a9447400e1.jpg", null, 1)); list.add(new DataBean("https://img.zcool.cn/community/016a2256fb63006ac7257948f83349.jpg", null, 1)); return list; } public static List getVideos() { List list = new ArrayList<>(); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4", null, 0)); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/18/mp4/190318231014076505.mp4", null, 0)); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/18/mp4/190318214226685784.mp4", null, 0)); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/19/mp4/190319125415785691.mp4", null, 0)); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/14/mp4/190314223540373995.mp4", null, 0)); list.add(new DataBean("http://vfx.mtime.cn/Video/2019/03/14/mp4/190314102306987969.mp4", null, 0)); return list; } public static List getColors(int size) { List list = new ArrayList<>(); for(int i = 0; i < size; i++) { list.add(getRandColor()); } return list; } /** * 获取十六进制的颜色代码.例如 "#5A6677" * 分别取R、G、B的随机值,然后加起来即可 * * @return String */ public static String getRandColor() { String R, G, B; Random random = new Random(); R = Integer.toHexString(random.nextInt(256)).toUpperCase(); G = Integer.toHexString(random.nextInt(256)).toUpperCase(); B = Integer.toHexString(random.nextInt(256)).toUpperCase(); R = R.length() == 1 ? "0" + R : R; G = G.length() == 1 ? "0" + G : G; B = B.length() == 1 ? "0" + B : B; return "#" + R + G + B; } } ================================================ FILE: app/src/main/java/com/test/banner/indicator/NumIndicator.java ================================================ package com.test.banner.indicator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import com.youth.banner.indicator.BaseIndicator; import com.youth.banner.util.BannerUtils; /** * 自定义数字指示器demo,比较简单,具体的自己发挥 * * 这里没有用的自定义属性的参数,可以考虑加上 */ public class NumIndicator extends BaseIndicator { private int width; private int height; private int radius; public NumIndicator(Context context) { this(context, null); } public NumIndicator(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NumIndicator(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint.setTextSize(BannerUtils.dp2px(10)); mPaint.setTextAlign(Paint.Align.CENTER); width = (int) BannerUtils.dp2px(30); height = (int) BannerUtils.dp2px(15); radius = (int) BannerUtils.dp2px(20); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = config.getIndicatorSize(); if (count <= 1) { return; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int count = config.getIndicatorSize(); if (count <= 1) { return; } RectF rectF = new RectF(0, 0, width, height); mPaint.setColor(Color.parseColor("#70000000")); canvas.drawRoundRect(rectF, radius, radius, mPaint); String text = config.getCurrentPosition() + 1 + "/" + count; mPaint.setColor(Color.WHITE); canvas.drawText(text, width / 2, (float) (height * 0.7), mPaint); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/BannerFragment.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.test.banner.R; import com.test.banner.adapter.ImageNetAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.indicator.RectangleIndicator; import com.youth.banner.util.BannerUtils; import butterknife.BindView; import butterknife.ButterKnife; public class BannerFragment extends Fragment { @BindView(R.id.banner) Banner banner; public static Fragment newInstance() { return new BannerFragment(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.banner, container, false); ButterKnife.bind(this, view); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); banner.setAdapter(new ImageNetAdapter(DataBean.getTestData3())); banner.setIndicator(new RectangleIndicator(getActivity())); banner.setIndicatorSpace((int) BannerUtils.dp2px(4)); banner.setIndicatorRadius(0); } @Override public void onStart() { super.onStart(); banner.start(); } @Override public void onStop() { super.onStop(); banner.stop(); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/BannerListFragment.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; import com.test.banner.adapter.MyRecyclerViewAdapter; import com.test.banner.util.ParentRecyclerView; import butterknife.BindView; import butterknife.ButterKnife; public class BannerListFragment extends Fragment { private static int index; @BindView(R.id.net_rv) RecyclerView recyclerView; @BindView(R.id.text) TextView text; public static Fragment newInstance(int i) { index = i; return new BannerListFragment(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.activity_recyclerview_banner, container, false); ButterKnife.bind(this,view); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); text.setText("当前页:"+index); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setAdapter(new MyRecyclerViewAdapter(getActivity())); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/BlankFragment.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; import com.test.banner.adapter.ImageNetAdapter; import com.test.banner.adapter.MyRecyclerViewAdapter; import com.test.banner.bean.DataBean; import com.test.banner.indicator.NumIndicator; import com.youth.banner.Banner; import com.youth.banner.config.IndicatorConfig; import com.youth.banner.indicator.CircleIndicator; import com.youth.banner.util.BannerUtils; import butterknife.BindView; import butterknife.ButterKnife; public class BlankFragment extends Fragment { public static Fragment newInstance() { return new BlankFragment(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.test,null); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); LinearLayout linearLayout = getView().findViewById(R.id.ll_view); //通过new的方式创建banner Banner banner = new Banner(getActivity()); banner.setAdapter(new ImageNetAdapter(DataBean.getTestData3())); banner.addBannerLifecycleObserver(this); banner.setIndicator(new CircleIndicator(getActivity())); //将banner加入到父容器中,实际使用不一定一样 linearLayout.addView(banner, LinearLayout.LayoutParams.MATCH_PARENT, (int) BannerUtils.dp2px(120)); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/ConstraintLayoutBannerActivity.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import androidx.appcompat.app.AppCompatActivity; import com.test.banner.R; import com.test.banner.adapter.ImageAdapter; import com.test.banner.adapter.ImageNetAdapter; import com.test.banner.adapter.ImageTitleAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.config.BannerConfig; import com.youth.banner.config.IndicatorConfig; import com.youth.banner.indicator.CircleIndicator; import com.youth.banner.util.BannerUtils; import butterknife.BindView; import butterknife.ButterKnife; public class ConstraintLayoutBannerActivity extends AppCompatActivity { private static final String TAG = "banner_log"; @BindView(R.id.banner) Banner banner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_constraint_layout_banner); ButterKnife.bind(this); banner.setAdapter(new ImageTitleAdapter(DataBean.getTestData())); banner.setIndicator(new CircleIndicator(this)); banner.setIndicatorSelectedColorRes(R.color.main_color); banner.setIndicatorGravity(IndicatorConfig.Direction.RIGHT); banner.setIndicatorMargins(new IndicatorConfig.Margins(0, 0, BannerConfig.INDICATOR_MARGIN, (int) BannerUtils.dp2px(12))); banner.addBannerLifecycleObserver(this); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/GalleryActivity.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.view.View; import com.test.banner.R; import com.test.banner.adapter.ImageAdapter; import com.test.banner.adapter.ImageNetAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.indicator.CircleIndicator; import com.youth.banner.indicator.DrawableIndicator; import com.youth.banner.transformer.AlphaPageTransformer; import androidx.appcompat.app.AppCompatActivity; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; public class GalleryActivity extends AppCompatActivity { @BindView(R.id.banner1) Banner mBanner1; @BindView(R.id.banner2) Banner mBanner2; @BindView(R.id.indicator) DrawableIndicator indicator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gallery); ButterKnife.bind(this); /** * 画廊效果 */ mBanner1.setAdapter(new ImageAdapter(DataBean.getTestData2())); mBanner1.setIndicator(new CircleIndicator(this)); //添加画廊效果 mBanner1.setBannerGalleryEffect(50, 10); //(可以和其他PageTransformer组合使用,比如AlphaPageTransformer,注意但和其他带有缩放的PageTransformer会显示冲突) //添加透明效果(画廊配合透明效果更棒) //mBanner1.addPageTransformer(new AlphaPageTransformer()); /** * 魅族效果 */ mBanner2.setAdapter(new ImageAdapter(DataBean.getTestData())); mBanner2.setIndicator(indicator,false); //添加魅族效果 mBanner2.setBannerGalleryMZ(20); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/RecyclerViewBannerActivity.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.view.View; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; import com.test.banner.adapter.MyRecyclerViewAdapter; import butterknife.BindView; import butterknife.ButterKnife; public class RecyclerViewBannerActivity extends AppCompatActivity { @BindView(R.id.net_rv) RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recyclerview_banner); ButterKnife.bind(this); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new MyRecyclerViewAdapter(this)); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/TVActivity.java ================================================ package com.test.banner.ui; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import com.test.banner.R; import com.test.banner.adapter.ImageAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.indicator.CircleIndicator; import com.youth.banner.util.BannerUtils; import butterknife.BindView; import butterknife.ButterKnife; public class TVActivity extends AppCompatActivity { private static final String TAG = "banner_log"; @BindView(R.id.banner) Banner banner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_t_v); ButterKnife.bind(this); banner.setAdapter(new ImageAdapter(DataBean.getTestData())); banner.setIndicator(new CircleIndicator(this)); banner.isAutoLoop(false); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { int count = banner.getItemCount(); switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: Log.d(TAG, "向左"); int prev = (banner.getCurrentItem() - 1) % count; if (prev == 0) { prev = banner.getRealCount(); } else if (prev == count - 1) { prev = 1; } banner.setCurrentItem(prev, false); break; case KeyEvent.KEYCODE_DPAD_RIGHT: Log.d(TAG, "向右"); int next = (banner.getCurrentItem() + 1) % count; if (next == 0) { next = banner.getRealCount(); } else if (next == count - 1) { next = 1; } banner.setCurrentItem(next, false); break; } //如果没有设置指示器,就不用执行下面两行 int real = BannerUtils.getRealPosition(banner.isInfiniteLoop(), banner.getCurrentItem(), banner.getRealCount()); banner.getIndicator().onPageSelected(real); return super.onKeyDown(keyCode, event); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/TouTiaoActivity.java ================================================ package com.test.banner.ui; import androidx.appcompat.app.AppCompatActivity; import butterknife.BindView; import butterknife.ButterKnife; import android.os.Bundle; import com.google.android.material.snackbar.Snackbar; import com.test.banner.R; import com.test.banner.adapter.TopLineAdapter; import com.test.banner.bean.DataBean; import com.youth.banner.Banner; import com.youth.banner.transformer.ZoomOutPageTransformer; import com.youth.banner.util.LogUtils; public class TouTiaoActivity extends AppCompatActivity { @BindView(R.id.banner) Banner banner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tou_tiao); ButterKnife.bind(this); //实现1号店和淘宝头条类似的效果 banner.setAdapter(new TopLineAdapter(DataBean.getTestData2())) .setOrientation(Banner.VERTICAL) .setPageTransformer(new ZoomOutPageTransformer()) .setOnBannerListener((data, position) -> { Snackbar.make(banner, ((DataBean) data).title, Snackbar.LENGTH_SHORT).show(); LogUtils.d("position:" + position); }); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/VideoActivity.java ================================================ package com.test.banner.ui; import android.os.Bundle; import android.util.Log; import com.shuyu.gsyvideoplayer.GSYVideoManager; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import com.test.banner.R; import com.test.banner.adapter.MultipleTypesAdapter; import com.test.banner.bean.DataBean; import com.test.banner.indicator.NumIndicator; import com.test.banner.viewholder.VideoHolder; import com.youth.banner.Banner; import com.youth.banner.config.IndicatorConfig; import com.youth.banner.listener.OnPageChangeListener; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.RecyclerView; import butterknife.BindView; import butterknife.ButterKnife; /** * 仿淘宝商品详情,banner第一个放视频,然后首尾不能自己滑动,加上自定义数字指示器 */ public class VideoActivity extends AppCompatActivity { @BindView(R.id.banner) Banner banner; StandardGSYVideoPlayer player; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); ButterKnife.bind(this); banner.addBannerLifecycleObserver(this) .setAdapter(new MultipleTypesAdapter(this, DataBean.getTestDataVideo())) .setIndicator(new NumIndicator(this)) .setIndicatorGravity(IndicatorConfig.Direction.RIGHT) .addOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { stopVideo(position); } @Override public void onPageSelected(int position) { Log.e("--","position:"+position); stopVideo(position); } @Override public void onPageScrollStateChanged(int state) { } }); } private void stopVideo(int position) { if (player == null) { RecyclerView.ViewHolder viewHolder = banner.getAdapter().getViewHolder(); if (viewHolder instanceof VideoHolder) { VideoHolder holder = (VideoHolder) viewHolder; player = holder.player; if (position != 0) { player.onVideoPause(); } } }else { if (position != 0) { player.onVideoPause(); } } } @Override protected void onPause() { super.onPause(); if (player != null) player.onVideoPause(); } @Override protected void onResume() { super.onResume(); if (player != null) player.onVideoResume(); } @Override protected void onDestroy() { super.onDestroy(); GSYVideoManager.releaseAllVideos(); } @Override public void onBackPressed() { //释放所有 if (player != null) player.setVideoAllCallBack(null); super.onBackPressed(); } } ================================================ FILE: app/src/main/java/com/test/banner/ui/Vp2FragmentRecyclerviewActivity.java ================================================ package com.test.banner.ui; import android.os.Bundle; import com.google.android.material.tabs.TabLayout; import com.test.banner.R; import com.test.banner.adapter.ImageAdapter; import com.test.banner.bean.DataBean; import com.test.banner.util.TabLayoutMediator; import com.youth.banner.Banner; import com.youth.banner.indicator.CircleIndicator; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; import butterknife.BindView; import butterknife.ButterKnife; public class Vp2FragmentRecyclerviewActivity extends AppCompatActivity { @BindView(R.id.vp2) ViewPager2 viewPager2; @BindView(R.id.tab_layout) TabLayout mTabLayout; @BindView(R.id.banner) Banner mBanner; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_vp2_fragment_recyclerview); ButterKnife.bind(this); viewPager2.setAdapter(new FragmentStateAdapter(this) { @NonNull @Override public Fragment createFragment(int position) { if (position == 0) { return BannerListFragment.newInstance(position); } else if (position == 1) { return BlankFragment.newInstance(); } else { return BannerFragment.newInstance(); } } @Override public int getItemCount() { return 3; } }); new TabLayoutMediator(mTabLayout, viewPager2, (tab, position) -> { tab.setText("页面" + position); }).attach(); mBanner.addBannerLifecycleObserver(this) .setAdapter(new ImageAdapter(DataBean.getTestData())) .setIntercept(false) .setIndicator(new CircleIndicator(this)); } } ================================================ FILE: app/src/main/java/com/test/banner/util/ParentRecyclerView.java ================================================ package com.test.banner.util; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; public class ParentRecyclerView extends RecyclerView { public ParentRecyclerView(@NonNull Context context) { super(context); } public ParentRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public ParentRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private float mStartX, mStartY; @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = ev.getX(); mStartY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float endX = ev.getX(); float endY = ev.getY(); float distanceX = Math.abs(endX - mStartX); float distanceY = Math.abs(endY - mStartY); getParent().requestDisallowInterceptTouchEvent(!(distanceX > 4 && distanceX > distanceY)); break; } return super.dispatchTouchEvent(ev); } } ================================================ FILE: app/src/main/java/com/test/banner/util/TabLayoutMediator.java ================================================ package com.test.banner.util; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING; /** * A mediator to link a TabLayout with a ViewPager2. The mediator will synchronize the ViewPager2's * position with the selected tab when a tab is selected, and the TabLayout's scroll position when * the user drags the ViewPager2. * * Establish the link by creating an instance of this class, make sure the ViewPager2 has an adapter * and then call {@link #attach()} on it. When creating an instance of this class, you must supply * an implementation of {@link OnConfigureTabCallback} in which you set the text of the tab, and/or * perform any styling of the tabs that you require. * * @hide */ @RestrictTo(LIBRARY_GROUP) public final class TabLayoutMediator { private final @NonNull TabLayout mTabLayout; private final @NonNull ViewPager2 mViewPager; private final boolean mAutoRefresh; private final OnConfigureTabCallback mOnConfigureTabCallback; private RecyclerView.Adapter mAdapter; private boolean mAttached; private TabLayoutOnPageChangeCallback mOnPageChangeCallback; private TabLayout.OnTabSelectedListener mOnTabSelectedListener; private RecyclerView.AdapterDataObserver mPagerAdapterObserver; /** * A callback interface that must be implemented to set the text and styling of newly created * tabs. */ public interface OnConfigureTabCallback { /** * Called to configure the tab for the page at the specified position. Typically calls * {@link TabLayout.Tab#setText(CharSequence)}, but any form of styling can be applied. * * @param tab The Tab which should be configured to represent the title of the item at the * given position in the data set. * @param position The position of the item within the adapter's data set. */ void onConfigureTab(@NonNull TabLayout.Tab tab, int position); } /** * Creates a TabLayoutMediator to synchronize a TabLayout and a ViewPager2 together. It will * update the tabs automatically when the data set of the view pager's adapter changes. The link * will be established after {@link #attach()} is called. * * @param tabLayout The tab bar to link * @param viewPager The view pager to link */ public TabLayoutMediator(@NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager, @NonNull OnConfigureTabCallback onConfigureTabCallback) { this(tabLayout, viewPager, true, onConfigureTabCallback); } /** * Creates a TabLayoutMediator to synchronize a TabLayout and a ViewPager2 together. If {@code * autoRefresh} is true, it will update the tabs automatically when the data set of the view * pager's adapter changes. The link will be established after {@link #attach()} is called. * * @param tabLayout The tab bar to link * @param viewPager The view pager to link * @param autoRefresh If {@code true}, will recreate all tabs when the data set of the view * pager's adapter changes. */ public TabLayoutMediator(@NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager, boolean autoRefresh, @NonNull OnConfigureTabCallback onConfigureTabCallback) { mTabLayout = tabLayout; mViewPager = viewPager; mAutoRefresh = autoRefresh; mOnConfigureTabCallback = onConfigureTabCallback; } /** * Link the TabLayout and the ViewPager2 together. * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no * adapter. */ public void attach() { if (mAttached) { throw new IllegalStateException("TabLayoutMediator is already attached"); } mAdapter = mViewPager.getAdapter(); if (mAdapter == null) { throw new IllegalStateException("TabLayoutMediator attached before ViewPager2 has an " + "adapter"); } mAttached = true; // Add our custom OnPageChangeCallback to the ViewPager mOnPageChangeCallback = new TabLayoutOnPageChangeCallback(mTabLayout); mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback); // Now we'll add a tab selected listener to set ViewPager's current item mOnTabSelectedListener = new ViewPagerOnTabSelectedListener(mViewPager); mTabLayout.addOnTabSelectedListener(mOnTabSelectedListener); // Now we'll populate ourselves from the pager adapter, adding an observer if // autoRefresh is enabled if (mAutoRefresh) { // Register our observer on the new adapter mPagerAdapterObserver = new PagerAdapterObserver(); mAdapter.registerAdapterDataObserver(mPagerAdapterObserver); } populateTabsFromPagerAdapter(); // Now update the scroll position to match the ViewPager's current item mTabLayout.setScrollPosition(mViewPager.getCurrentItem(), 0f, true); } /** * Unlink the TabLayout and the ViewPager */ public void detach() { mAdapter.unregisterAdapterDataObserver(mPagerAdapterObserver); mTabLayout.removeOnTabSelectedListener(mOnTabSelectedListener); mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback); mPagerAdapterObserver = null; mOnTabSelectedListener = null; mOnPageChangeCallback = null; mAttached = false; } @SuppressWarnings("WeakerAccess") void populateTabsFromPagerAdapter() { mTabLayout.removeAllTabs(); if (mAdapter != null) { int adapterCount = mAdapter.getItemCount(); for (int i = 0; i < adapterCount; i++) { TabLayout.Tab tab = mTabLayout.newTab(); mOnConfigureTabCallback.onConfigureTab(tab, i); mTabLayout.addTab(tab, false); } // Make sure we reflect the currently set ViewPager item if (adapterCount > 0) { int currItem = mViewPager.getCurrentItem(); if (currItem != mTabLayout.getSelectedTabPosition()) { mTabLayout.getTabAt(currItem).select(); } } } } /** * A {@link ViewPager2.OnPageChangeCallback} class which contains the necessary calls back to * the provided {@link TabLayout} so that the tab position is kept in sync. * *

This class stores the provided TabLayout weakly, meaning that you can use {@link * ViewPager2#registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback)} without removing * the callback and not cause a leak. */ private static class TabLayoutOnPageChangeCallback extends ViewPager2.OnPageChangeCallback { private final WeakReference mTabLayoutRef; private int mPreviousScrollState; private int mScrollState; TabLayoutOnPageChangeCallback(TabLayout tabLayout) { mTabLayoutRef = new WeakReference<>(tabLayout); reset(); } @Override public void onPageScrollStateChanged(final int state) { mPreviousScrollState = mScrollState; mScrollState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { TabLayout tabLayout = mTabLayoutRef.get(); if (tabLayout != null) { // Only update the text selection if we're not settling, or we are settling after // being dragged boolean updateText = mScrollState != SCROLL_STATE_SETTLING || mPreviousScrollState == SCROLL_STATE_DRAGGING; // Update the indicator if we're not settling after being idle. This is caused // from a setCurrentItem() call and will be handled by an animation from // onPageSelected() instead. boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING && mPreviousScrollState == SCROLL_STATE_IDLE); setScrollPosition(tabLayout, position, positionOffset, updateText, updateIndicator); } } @Override public void onPageSelected(final int position) { TabLayout tabLayout = mTabLayoutRef.get(); if (tabLayout != null && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) { // Select the tab, only updating the indicator if we're not being dragged/settled // (since onPageScrolled will handle that). boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE || (mScrollState == SCROLL_STATE_SETTLING && mPreviousScrollState == SCROLL_STATE_IDLE); selectTab(tabLayout, tabLayout.getTabAt(position), updateIndicator); } } void reset() { mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE; } } // region Reflective calls // Temporarily call methods TabLayout.setScrollPosition(int, float, boolean, boolean) and // TabLayout.selectTab(TabLayout.Tab, boolean) through reflection, until they have been made // public in the Material Design Components library. private static Method sSetScrollPosition; private static Method sSelectTab; private static final String SET_SCROLL_POSITION_NAME = "TabLayout.setScrollPosition(int, float," + " boolean, boolean)"; private static final String SELECT_TAB_NAME = "TabLayout.selectTab(TabLayout.Tab, boolean)"; static { try { sSetScrollPosition = TabLayout.class.getDeclaredMethod("setScrollPosition", int.class, float.class, boolean.class, boolean.class); sSetScrollPosition.setAccessible(true); sSelectTab = TabLayout.class.getDeclaredMethod("selectTab", TabLayout.Tab.class, boolean.class); sSelectTab.setAccessible(true); } catch (NoSuchMethodException e) { throw new IllegalStateException("Can't reflect into method TabLayout" + ".setScrollPosition(int, float, boolean, boolean)"); } } @SuppressWarnings("WeakerAccess") static void setScrollPosition(TabLayout tabLayout, int position, float positionOffset, boolean updateSelectedText, boolean updateIndicatorPosition) { try { if (sSetScrollPosition != null) { sSetScrollPosition.invoke(tabLayout, position, positionOffset, updateSelectedText, updateIndicatorPosition); } else { throwMethodNotFound(SET_SCROLL_POSITION_NAME); } } catch (Exception e) { throwInvokeFailed(SET_SCROLL_POSITION_NAME); } } @SuppressWarnings("WeakerAccess") static void selectTab(TabLayout tabLayout, TabLayout.Tab tab, boolean updateIndicator) { try { if (sSelectTab != null) { sSelectTab.invoke(tabLayout, tab, updateIndicator); } else { throwMethodNotFound(SELECT_TAB_NAME); } } catch (Exception e) { throwInvokeFailed(SELECT_TAB_NAME); } } private static void throwMethodNotFound(String method) { throw new IllegalStateException("Method " + method + " not found"); } private static void throwInvokeFailed(String method) { throw new IllegalStateException("Couldn't invoke method " + method); } // endregion /** * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back to * the provided {@link ViewPager2} so that the tab position is kept in sync. */ private static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener { private final ViewPager2 mViewPager; ViewPagerOnTabSelectedListener(ViewPager2 viewPager) { this.mViewPager = viewPager; } @Override public void onTabSelected(TabLayout.Tab tab) { mViewPager.setCurrentItem(tab.getPosition(), true); } @Override public void onTabUnselected(TabLayout.Tab tab) { // No-op } @Override public void onTabReselected(TabLayout.Tab tab) { // No-op } } private class PagerAdapterObserver extends RecyclerView.AdapterDataObserver { PagerAdapterObserver() {} @Override public void onChanged() { populateTabsFromPagerAdapter(); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { populateTabsFromPagerAdapter(); } } } ================================================ FILE: app/src/main/java/com/test/banner/viewholder/ImageHolder.java ================================================ package com.test.banner.viewholder; import android.view.View; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; public class ImageHolder extends RecyclerView.ViewHolder { public ImageView imageView; public ImageHolder(@NonNull View view) { super(view); this.imageView = (ImageView) view; } } ================================================ FILE: app/src/main/java/com/test/banner/viewholder/ImageTitleHolder.java ================================================ package com.test.banner.viewholder; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; public class ImageTitleHolder extends RecyclerView.ViewHolder { public ImageView imageView; public TextView title; public ImageTitleHolder(@NonNull View view) { super(view); imageView = view.findViewById(R.id.image); title = view.findViewById(R.id.bannerTitle); } } ================================================ FILE: app/src/main/java/com/test/banner/viewholder/TitleHolder.java ================================================ package com.test.banner.viewholder; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.test.banner.R; public class TitleHolder extends RecyclerView.ViewHolder { public TextView title; public TitleHolder(@NonNull View view) { super(view); title = view.findViewById(R.id.bannerTitle); } } ================================================ FILE: app/src/main/java/com/test/banner/viewholder/VideoHolder.java ================================================ package com.test.banner.viewholder; import android.view.View; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer; import com.test.banner.R; public class VideoHolder extends RecyclerView.ViewHolder { public StandardGSYVideoPlayer player; public VideoHolder(@NonNull View view) { super(view); player = view.findViewById(R.id.player); } } ================================================ FILE: app/src/main/res/drawable/background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/default_selecter.xml ================================================ ================================================ FILE: app/src/main/res/drawable/green.xml ================================================ ================================================ FILE: app/src/main/res/drawable/selected_radius.xml ================================================ ================================================ FILE: app/src/main/res/drawable/unselected_radius.xml ================================================ ================================================ FILE: app/src/main/res/drawable/white.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_constraint_layout_banner.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_gallery.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================