转:Firemonkey使用Android原生控件一些注意事项

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

原文地址:https://www.cnblogs.com/timba1322/p/12678272.html