很多展示类的app,会使用到banner图,其中也不乏想要图片+视频混合轮播的需求,例如客显屏系统,就使用到了该种功能,下面会简单列举实现流程,结尾附带demo源码
(一)权限申请
1.manifest文件
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2.MainActivity动态注册
2.1 引用permissionX
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
2.2 动态申请
private static final String PERMISSION_WRITE_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private static final String PERMISSION_READ_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;
private void initPermission() {
PermissionX.init(this)
.permissions(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
.onExplainRequestReason((scope, deniedList, beforeRequest) -> scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "确定"))
.onForwardToSettings((scope, deniedList) -> scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "确定"))
.request((allGranted, grantedList, deniedList) -> {
if (allGranted) {
//权限全部获取,执行后续操作
}
});
}
2.3判断是否已有权限
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(PERMISSION_READ_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(PERMISSION_WRITE_STORAGE) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
(二)使用HBanner
1.将hbanner相关代码复制到工程中
说明:Hbanner是一个开源项目,可以引用代码库,但是对于这种自定义View的库,并不一定所有的设计都符合自己的要求,可能会需要做出一定的本地修改,所以建议如果方便的话就拷贝代码而非引用代码库
2.xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.hjly.hbannerdemo.hbanner.view.BannerViewPager
android:id="@+id/bannerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
3.MainActivity
//定义一个资源文件存储路径
public static String outputDir = "/sdcard/Download/HbannerDemo/resource/";
private HBanner hBanner;
private BannerViewPager bannerView;
bannerView = findViewById(R.id.bannerView);
bannerView.setScrollable(false);
private void init() {
try {
if (hBanner != null) {
hBanner.release();
hBanner = null;
}
try {
hBanner = HBanner.create(bannerView);
} catch (Exception e) {
e.printStackTrace();
return;
}
List<MediaFile> mediaFiles = AppUtil.getAllFiles(outputDir);
if (ListUtil.isEmpty(mediaFiles)) {
Toast.makeText(this, "本地无媒体数据", Toast.LENGTH_SHORT).show();
return;
} else {
//每隔3秒切换一次图片
int duration = 3;
List<SubView> data = new ArrayList<>();
for (MediaFile mediaFile : mediaFiles) {
if (mediaFile.getFileName().endsWith("png") || mediaFile.getFileName().endsWith("jpg")) {
data.add(new ImageSubView.Builder(getBaseContext())
.file(new File(mediaFile.getFilePath()))
.gravity(ImageView.ScaleType.FIT_XY)
.duration(duration * 1000)
.build());
} else if (mediaFile.getFileName().endsWith("mp4")) {
data.add(new VideoSubView.Builder(getBaseContext())
.file(new File(mediaFile.getFilePath()))
.gravity(VideoViewType.FULL)
.playOffset(600)//让缓存图片显示600ms后再播放,可解决videoview启动时候闪烁问题
.isSub(false)
.build());
}
}
if (data.size() > 0) {
hBanner.sources(data);
bannerView.setPageTransformer(true, new DefaultTransformer());
hBanner.play(true);
} else {
Toast.makeText(this, "解析媒体数据为空", Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 中断资源播放
*/
private void stopBanner() {
if (hBanner != null) {
hBanner.release();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopBanner();
}
关键点:
如果按照上面的写法,在OnCreate中完成初始化之后,直接调用init方法,那么就会轮播功能没有实现,看日志是报了如下的错误
java.lang.IllegalArgumentException: your viewPager has not attached,it will can not get the handler!
这个问题也是列表布局里非常常见的错误,网上百度的说法是,列表数据没有预加载,然后让弄懒加载配置之类的,不过收效甚微
这里可以尝试如下做法
1.Activity实现OnGlobalLayoutListener接口
public class MainActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener {
2.onCreate
binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(this);
3.重写方法
@Override
public void onGlobalLayout() {
binding.getRoot().getViewTreeObserver().removeOnGlobalLayoutListener(this);
init();
}
说明:实现上述接口一定要在OnCreate中增加addOnGlobalLayoutListener,并且在onGlobalLayout调用对应的removeOnGlobalLayoutListener,否则init方法将会无限重复调用
写在最后:
本demo中实现的功能需要手动将图片、视频资源放置在"/sdcard/Download/HbannerDemo/resource/"目录下,实际中,资源文件需要从后台下载并且通过文件IO操作生成文件夹以及文件并且解压资源到路径下,这里为了方便就节省了该步骤,直接手动创建目录,放置资源文件
至此,功能实现