zoukankan      html  css  js  c++  java
  • 转: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

  • 相关阅读:
    OpenCV4Android——No implementation found for native Lorg/opencv/core/Mat;.n_Mat ()J
    The method onClick(View) of type new View.OnClickListener(){} must override a superclass
    Android与OpenCV——重新下载安装和OpenCV匹配的Android开发环境
    45_拍照
    32_文件断点上传器
    31_多线程断点下载器
    Eclipse的Servers视图中无法添加Tomcat
    23_网络通信之网络图片查看器
    C语言 · 时间转换
    C语言 · 4_2找公倍数
  • 原文地址:https://www.cnblogs.com/timba1322/p/12678272.html
Copyright © 2011-2022 走看看