Firemonkey使用MediaPlayer时是没有事件的,需要自己处理,一般在Windows下就算了,大把的播放器可以用,但在Android下比较麻烦,本人尝试用FFMpeg,但编译的so库只支持v7a架构,低级设备支持不好,而且没有编译硬件解码,所以播放时漏帧严重,MediaPlayer也不是个好东西,10.3以下的顶层遮盖问题,10.3以上取不到时间、状态(始终是Stop),所以要解决好几个问题才能满足本人的要求:(本人要求从头播放一个小视频,结束后再播放下一个,右上角可以显示一个图标,下面要显示一个滚动字幕)
研究了一下FMX.Media.Android.pas,决定还是自己封装一个JVideoView
一、播放器事件Listener
网上找了一圈在CSDN上ssxbxk作者倒是写了实现JNI监听器的方法,可惜没写具体使用方法,失败很多次最后才成功。
步骤如下:
1、Delplhi 实现Java Jar包中的Listener,这是ssxbxk作者的原文
2、我要实现的是播放器完成事件,所以使用是JVideoView.setOnCompletionListener
按Ctrl+鼠标左键,找到Androidapi.JNI.Media.pas中的声明,XE10.2.3版本中在2979行位置
[JavaSignature(‘android/media/MediaPlayer$OnCompletionListener‘)]
JMediaPlayer_OnCompletionListener = interface(IJavaInstance)
[‘{855040E1-8E41-40EE-B36F-06C212B8AC81}‘]
procedure onCompletion(mp: JMediaPlayer); cdecl;
end;
说明只有一个onCompletion方法,下面要实现一个新的类
type
TOnCompletionListener = class(TJavaLocal, JMediaPlayer_OnCompletionListener)
private
[Weak] FParent: TFrmMain;
public
constructor Create(Parent: TFrmMain);
procedure onCompletion(mp: JMediaPlayer); cdecl;
end;
//--以下是实现部分
{ TOnCompletionListener }
constructor TOnCompletionListener.Create(Parent: TFrmMain);
begin
inherited Create;
FParent := Parent;
end;
procedure TOnCompletionListener.onCompletion(mp: JMediaPlayer);
begin
CallInUIThreadAndWaitFinishing(
procedure
begin
FParent.OnCompleteion(FParent);//调用TFrmMain中的NotifyEvent事件
end);
end;
最后使用这个监听器实例,上面那个blog中没有写这部分
var
completion_event: TOnCompletionListener = nil;//定义成全局的,反正不能定义在栈里(方法的Begin前面)
//--创建监听器实例,再设置成JViewView里
completion_event := TOnCompletionListener.Create(Self);
FVideoView.setOnCompletionListener(completion_event);//生成播放完成事件类实例
二、在JViewView上插入原生的JImageView,用来显示图片
说实话,不会点Android,根本没法在FireMonkey下搞原生控件,言归正传,查看FMX.Media.Android.pas发现,JViewView是直接放在JNativeLayout上面的(原生布局控件,Delphi实现的),这个布局控件里只能放一个控件,用SetControl方法(1513行),我要放图片原生控件没法放了,所以要先创建一个其它的布局控件,然后再方向JViewView、JImageView,我这里放的在相对布局JRelativeLayout,具体实现:
FNativeLayout := TJNativeLayout.JavaClass.init(TAndroidHelper.Activity,
MainActivity.getWindow.getDecorView.getWindowToken);//创建原生控件
FNativeLayout.setPosition(0, 0);//设置位置
//FNativeLayout.setSize(XXWidth, XXHeight);//在组件大小变化时设置原生控件的Size,参考FMX.Media.Android.pas中的TAndroidVideo.RealignView方法
var
params: JRelativeLayout_LayoutParams;//布局参数
begin
FRelativeLayout := TJRelativeLayout.JavaClass.init(TAndroidHelper.Activity);//创建布局控件
params := TJRelativeLayout_LayoutParams.JavaClass.init(TJViewGroup_LayoutParams.JavaClass.MATCH_PARENT, TJViewGroup_LayoutParams.JavaClass.MATCH_PARENT);//设置为纵横都拉伸到父控件
FRelativeLayout.setLayoutParams(params);
end;
下一步将相对布局放到原生控件里
FNativeLayout.setControl(FRelativeLayout);//添加到原生控件上
再将其它控件放到相对布局里(相对布局里可以放多个控件)
var
params: JRelativeLayout_LayoutParams;
begin
//创建JImageView,并设置宽高,设置为右上对齐
FImageView := TJImageView.JavaClass.init(TAndroidHelper.Activity);
params := TJRelativeLayout_LayoutParams.JavaClass.init(W, H);
params.addRule(TJRelativeLayout.JavaClass.ALIGN_PARENT_TOP);
params.addRule(TJRelativeLayout.JavaClass.ALIGN_PARENT_RIGHT);
params.topMargin := 30;
params.rightMargin := 30;
FImageView.setLayoutParams(params);
end;
function GetJBitmap(const Bitmap: TBitmap): JBitmap;
var
Surface: TBitmapSurface;
JavaBitmap: JBitmap;
begin
//FireMonkey的Bitmap转成JImageView需要的JBitmap
Result := nil;
Surface := TBitmapSurface.Create;
try
Surface.Assign(Bitmap);
JavaBitmap := TJBitmap.JavaClass.createBitmap(Surface.Width, Surface.Height, TJBitmap_Config.JavaClass.ARGB_8888);
if SurfaceToJBitmap(Surface, JavaBitmap) then
Result := JavaBitmap;
finally
Surface.DisposeOf;
end;
end;
//使用方法
procedure ShowImage(Bitmap: TBitmap);
var
bmp: JBitmap;
begin
bmp := GetJBitmap(Bitmap);
CallInUIThread(procedure
begin
FImageView.setImageBitmap(bmp);
end);
end;
三、滚动字幕
据说使用JTextView可以实现,写法与JImageView差不多,在单行且文字超出时可以自动滚动,关键是要重载isFocused方法,还有一些简单设定
public class AutoMarqueeTextView extends TextView {
@Override public boolean isFocused() {
//这个方法必须返回true,制造假象,当系统调用该方法的时候,会一直以为TextView已经获取了焦点
return true;
}
}
好像无法控制滚动速度,最重要的怎么用delphi来实现这个JAVA类的重载,没搞定啊,谁知道记得给我留言啊,多谢。
最终使用ImageView方式实现了滚动字幕,使用原生的JBitmap、JCanvas、JPaint进行自绘,最后将JBitmap设置到ImageView。
需要注意的是屏幕分辨率与字原生控件布局的影响,在FireMonkey中作用Java接口时以PX像素为单位,会导致在分辨率高的屏幕上图像显得小,跟Scale和DPI无关,Scale是屏幕长宽比,DPI是每英寸上像素点个数,这里说的分辨率是屏幕长宽像素个数,所以设置图像布局时的长宽使用屏幕长宽的百分比最合适,可以保证在其它分辨率的屏幕下显示效果一致。
Firemonkey使用MediaPlayer时是没有事件的,需要自己处理,一般在Windows下就算了,大把的播放器可以用,但在Android下比较麻烦,本人尝试用FFMpeg,但编译的so库只支持v7a架构,低级设备支持不好,而且没有编译硬件解码,所以播放时漏帧严重,MediaPlayer也不是个好东西,10.3以下的顶层遮盖问题,10.3以上取不到时间、状态(始终是Stop),所以要解决好几个问题才能满足本人的要求:(本人要求从头播放一个小视频,结束后再播放下一个,右上角可以显示一个图标,下面要显示一个滚动字幕)
研究了一下FMX.Media.Android.pas,决定还是自己封装一个JVideoView
一、播放器事件Listener
网上找了一圈在CSDN上ssxbxk作者倒是写了实现JNI监听器的方法,可惜没写具体使用方法,失败很多次最后才成功。
步骤如下:
1、Delplhi 实现Java Jar包中的Listener,这是ssxbxk作者的原文
2、我要实现的是播放器完成事件,所以使用是JVideoView.setOnCompletionListener
按Ctrl+鼠标左键,找到Androidapi.JNI.Media.pas中的声明,XE10.2.3版本中在2979行位置
[JavaSignature(‘android/media/MediaPlayer$OnCompletionListener‘)]
JMediaPlayer_OnCompletionListener = interface(IJavaInstance)
[‘{855040E1-8E41-40EE-B36F-06C212B8AC81}‘]
procedure onCompletion(mp: JMediaPlayer); cdecl;
end;
说明只有一个onCompletion方法,下面要实现一个新的类
type TOnCompletionListener = class(TJavaLocal, JMediaPlayer_OnCompletionListener)
private [Weak] FParent: TFrmMain;
public constructor Create(Parent: TFrmMain);
procedure onCompletion(mp: JMediaPlayer); cdecl;
end;
//--以下是实现部分
{ TOnCompletionListener }
constructor TOnCompletionListener.Create(Parent: TFrmMain);
begin
inherited Create;
FParent := Parent;
end;
procedure TOnCompletionListener.onCompletion(mp: JMediaPlayer);
begin
CallInUIThreadAndWaitFinishing( procedure begin FParent.OnCompleteion(FParent);
//调用TFrmMain中的NotifyEvent事件
end);
end;
最后使用这个监听器实例,上面那个blog中没有写这部分
var completion_event: TOnCompletionListener = nil;
//定义成全局的,反正不能定义在栈里(方法的Begin前面)
//--创建监听器实例,再设置成JViewView里completion_event := TOnCompletionListener.Create(Self);FVideoView.setOnCompletionListener(completion_event);//生成播放完成事件类实例二、在JViewView上插入原生的JImageView,用来显示图片
说实话,不会点Android,根本没法在FireMonkey下搞原生控件,言归正传,查看FMX.Media.Android.pas发现,JViewView是直接放在JNativeLayout上面的(原生布局控件,Delphi实现的),这个布局控件里只能放一个控件,用SetControl方法(1513行),我要放图片原生控件没法放了,所以要先创建一个其它的布局控件,然后再方向JViewView、JImageView,我这里放的在相对布局JRelativeLayout,具体实现:
FNativeLayout := TJNativeLayout.JavaClass.init(TAndroidHelper.Activity, MainActivity.getWindow.getDecorView.getWindowToken);//创建原生控件 FNativeLayout.setPosition(0, 0);//设置位置//FNativeLayout.setSize(XXWidth, XXHeight);//在组件大小变化时设置原生控件的Size,参考FMX.Media.Android.pas中的TAndroidVideo.RealignView方法var params: JRelativeLayout_LayoutParams;//布局参数begin FRelativeLayout := TJRelativeLayout.JavaClass.init(TAndroidHelper.Activity);//创建布局控件 params := TJRelativeLayout_LayoutParams.JavaClass.init(TJViewGroup_LayoutParams.JavaClass.MATCH_PARENT, TJViewGroup_LayoutParams.JavaClass.MATCH_PARENT);//设置为纵横都拉伸到父控件 FRelativeLayout.setLayoutParams(params);end;下一步将相对布局放到原生控件里
FNativeLayout.setControl(FRelativeLayout);//添加到原生控件上再将其它控件放到相对布局里(相对布局里可以放多个控件)
var params: JRelativeLayout_LayoutParams;begin //创建JImageView,并设置宽高,设置为右上对齐 FImageView := TJImageView.JavaClass.init(TAndroidHelper.Activity); params := TJRelativeLayout_LayoutParams.JavaClass.init(W, H); params.addRule(TJRelativeLayout.JavaClass.ALIGN_PARENT_TOP); params.addRule(TJRelativeLayout.JavaClass.ALIGN_PARENT_RIGHT); params.topMargin := 30; params.rightMargin := 30; FImageView.setLayoutParams(params);end;function GetJBitmap(const Bitmap: TBitmap): JBitmap;var Surface: TBitmapSurface; JavaBitmap: JBitmap;begin //FireMonkey的Bitmap转成JImageView需要的JBitmap Result := nil; Surface := TBitmapSurface.Create; try Surface.Assign(Bitmap); JavaBitmap := TJBitmap.JavaClass.createBitmap(Surface.Width, Surface.Height, TJBitmap_Config.JavaClass.ARGB_8888); if SurfaceToJBitmap(Surface, JavaBitmap) then Result := JavaBitmap; finally Surface.DisposeOf; end;end; //使用方法procedure ShowImage(Bitmap: TBitmap);var bmp: JBitmap;begin bmp := GetJBitmap(Bitmap); CallInUIThread(procedure begin FImageView.setImageBitmap(bmp); end);end;三、滚动字幕
据说使用JTextView可以实现,写法与JImageView差不多,在单行且文字超出时可以自动滚动,关键是要重载isFocused方法,还有一些简单设定
public class AutoMarqueeTextView extends TextView { @Override public boolean isFocused() { //这个方法必须返回true,制造假象,当系统调用该方法的时候,会一直以为TextView已经获取了焦点 return true; }}好像无法控制滚动速度,最重要的怎么用delphi来实现这个JAVA类的重载,没搞定啊,谁知道记得给我留言啊,多谢。
最终使用ImageView方式实现了滚动字幕,使用原生的JBitmap、JCanvas、JPaint进行自绘,最后将JBitmap设置到ImageView。
需要注意的是屏幕分辨率与字原生控件布局的影响,在FireMonkey中作用Java接口时以PX像素为单位,会导致在分辨率高的屏幕上图像显得小,跟Scale和DPI无关,Scale是屏幕长宽比,DPI是每英寸上像素点个数,这里说的分辨率是屏幕长宽像素个数,所以设置图像布局时的长宽使用屏幕长宽的百分比最合适,可以保证在其它分辨率的屏幕下显示效果一致。————————————————版权声明:本文为CSDN博主「cmd9x」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/cmd9x/java/article/details/86555223