Android 开发笔记

 
MessageDlg 询问提示框
 
示例:
MessageDlg('是否退出?',
System.UITypes.TMsgDlgType.mtConfirmation,
[System.UITypes.TMsgDlgBtn.mbOK, System.UITypes.TMsgDlgBtn.mbCancel],
0,procedure(const AResult: TModalResult)
Begin
if AResult= mrok then
Begin
//这里要把Close的操作再做一次
Login.Logined := False
End;
End
);

 

普通提示框
MessageDlg('Ok', System.UITypes.TMsgDlgType.mtConfirmation, [System.UITypes.TMsgDlgBtn.mbOk],  0, nil) ;
 
对话框类型:
mtwarning——含有感叹号的警告对话框
mterror——含有红色叉符号的错误对话框
mtinformation——含有蓝色i符号的信息对话框
mtconfirmation——含有绿色问号的确认对话框
mtcustom——不含图标的一般对话框,对话框的标题是程序的名称
 
按钮组中的按钮:
mbYes——mrYes或6
mbNo——mrNo或7
mbOk——mrOk或1
mbCancel——mrCancel或2
mbHelp——help按钮
mbAbort——mrAbort或3
mbRetry——mrRetry或4
mbIgnore——mrIgnore或5
mbAll——mrAll或8
mbNoToAll——9
mbYesToAll——10
 
 

Edit 常用属性
设置Edit的ReturnKeyType为Search,表示回车键上的字母设置为"Search”(根据情况设置类别)
Edit的KillFocusByReturn设置为True,表示按回车键,Edit就失去焦点,隐藏虚拟键盘,

调用手机浏览器打开网页
需要引用单元
{$IFDEF Android}
  Androidapi.Helpers,
  FMX.Helpers.Android, Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Net, Androidapi.JNI.JavaTypes,
{$ENDIF}
 
方法:
procedure TLogin.XOpenURL(AUrl: String);
{$IFDEF Android}
var
  Intent: JIntent;
{$ENDIF}
begin
 
{$IFDEF Android}
  Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW,
        TJnet_Uri.JavaClass.parse(StringToJString(AUrl)));
  SharedActivity.startActivity(Intent);
{$ENDIF}
 
end;

保持后台运行
if (Key = vkHardwareBack)
  //Windows下Escape键模拟返回键
  or (Key = vkEscape) then
  begin
 
    //返回  (返回的窗口不能为空, 主窗口和登录窗口 不允许返回)
    if (CurrentFrameHistroy.ToFrame<>nil)
    and (CurrentFrameHistroy.ToFrame<>LoginFrame) 
      and (CurrentFrameHistroy.ToFrame<>MainForm) then
    begin
 
      if CanReturnFrame(CurrentFrameHistroy) then
      begin
      //返回上一页
      HideFrame(CurrentFrameHistroy.ToFrame,hfcttBeforeReturnFrame);
      ReturnFrame(CurrentFrameHistroy);
 
      //表示不关闭APP
      Key:=0;
      KeyChar:=#0;
      end
      else
      begin
      //表示当前Frame不允许返回
      end;
 
    end
    else
    begin
      {$IFDEF ANDROID}
      //程序退到后台挂起,需要引用Androidapi.Helpers单元
      FMX.Types.Log.d('OrangeUI moveTaskToBack');
      SharedActivity.moveTaskToBack(False);
 
      //表示不关闭APP
      Key:=0;
      KeyChar:=#0;
      {$ENDIF}
    end;
 
  end;

INI文件操作 并保存当前目录  需引用单元 System.IOUtils
// 保存
  fIni := TiniFile.Create(TPath.GetPublicPath + '/SD_Config.ini') ;
  try
    fIni.WriteString('Server','Server', LoginFrame.edServer.Text);
    fIni.WriteString('Server','DataBase', LoginFrame.edDataBase.Text);
    fIni.WriteString('Server','UserName', LoginFrame.edSQLUser.Text);
    fIni.WriteString('Server','Password', LoginFrame.edSQLPwd.Text);
    //
    fIni.writeString('Login','User', AUser);
    fIni.writeString('Login','pwd', APwd);
  finally
    fIni.Free;
  end;

长按触发事件 OnGesture
必需先设置属性:  Touch--InteractiveGestures--LongTap 选中 
 
case EventInfo.GestureID of
    igiLongTap:
      begin
            // do
      end;
  end;

将Delphi 安装目录下的 consts.pas 单元 中 提示框的英文改为中文, 编译后 将DCU 复制到 delphi目录下的LIB
SMsgDlgWarning = 'Warning';
  SMsgDlgError = 'Error';
  SMsgDlgInformation = 'Information';
  SMsgDlgConfirm = 'Confirm';
  SMsgDlgYes = 'Yes';
  SMsgDlgNo = 'No';
  SMsgDlgOK = 'OK';
  SMsgDlgCancel = 'Cancel';
  SMsgDlgHelp = 'Help';
  SMsgDlgHelpNone = 'No help available';
  SMsgDlgHelpHelp = 'Help';
  SMsgDlgAbort = 'Abort';
  SMsgDlgRetry = 'Retry';
  SMsgDlgIgnore = 'Ignore';
  SMsgDlgAll = 'All';
  SMsgDlgNoToAll = 'No to All';
  SMsgDlgYesToAll = 'Yes to &All';
  SMsgDlgClose = 'Close';
  }
  SMsgDlgWarning  =  '警告';
  SMsgDlgError  =  '错误';
  SMsgDlgInformation  =  '信息';
  SMsgDlgConfirm  =  '确认';
  SMsgDlgYes  =  '是';
  SMsgDlgNo  =  '否';
  SMsgDlgOK  =  '确定';
  SMsgDlgCancel  =  '放弃';
  SMsgDlgHelp  =  '帮助';
  SMsgDlgHelpNone  =  '无效的帮助';
  SMsgDlgHelpHelp  =  '帮助';
  SMsgDlgAbort  =  '放弃';
  SMsgDlgRetry  =  '重试';
  SMsgDlgIgnore  =  '忽略';
  SMsgDlgAll  =  '全部';
  SMsgDlgNoToAll  =  '全不';
  SMsgDlgYesToAll  =  '全是';
  SMsgDlgClose = '关闭';
 

临时文件路径(支持安卓、IOS)
function GeFileName(const AFileName: string): string;
begin
{$IFDEF ANDROID}
  Result := TPath.GetTempPath + '/' + AFileName;
{$ELSE}
  {$IFDEF IOS}
    Result := TPath.GetHomePath + '/Documents/' + AFileName;
  {$ELSE}
    Result := AFileName;
  {$ENDIF}
{$ENDIF}
end;
 
IOUtils文件说明
路径类
TPath.GetTempPath; {获取临时文件夹路径}
TPath.GetTempFileName; {获取一个临时文件名}
TPath.GetPathRoot(); {提取盘符, 如: c:\}
TPath.GetDirectoryName(); {提取路径}
TPath.GetFileName(); {提取文件名}
TPath.GetExtension(); {提取扩展名}
TPath.GetFileNameWithoutExtension(); {提取无扩展名的文件名}
TPath.ChangeExtension(); {更换扩展名}
TPath.DriveExists(); {检查路径中的驱动器是否存在}
TPath.GetFullPath(); {根据相对路径给出全路径}
TPath.HasExtension(); {判断是否有扩展名}
TPath.IsPathRooted(); {判断是否是绝对路径}
TPath.Combine(); {结合路径}
TPath.GetRandomFileName; {产生一个随机文件名}
TPath.GetGUIDFileName(); {用于产生一个唯一的文件名, 布尔参数 决定名称中是否包含 -}
TPath.IsValidPathChar(); {判断给定的字符是否能用于路径名}
TPath.IsValidFileNameChar(); {判断给定的字符是否能用于文件名}
TPath.AltDirectorySeparatorChar; {Windows 下是 "\"}
TPath.AltDirectorySeparatorChar; {Windows 下是 "/"}
TPath.ExtensionSeparatorChar; {Windows 下是 "."}
TPath.PathSeparator; {Windows 下是 ";"}
TPath.VolumeSeparatorChar; {Windows 下是 ":"}
 
ExtractFilePath(TPath.GetSharedDownloadsPath + '\..') // 获取当前目录的上一级
 
//目录类
TDirectory.CreateDirectory(); {建立新目录}
TDirectory.Exists(); {判断文件夹是否存在}
TDirectory.IsEmpty(); {判断文件夹是否为空}
TDirectory.Copy(); {复制文件夹}
TDirectory.Move(); {移动文件夹}
TDirectory.Delete(); {删除文件夹, 第二个参数为 True 可删除 非空文件夹}
TDirectory.GetDirectoryRoot(); {获取目录的根盘符, 如: C:\}
TDirectory.GetCurrentDirectory; {获取当前目录}
TDirectory.SetCurrentDirectory(); {设置当前目录}
TDirectory.GetLogicalDrives; {获取驱动器列表; 下有举例}
TDirectory.GetAttributes(); {获取文件夹属性, 譬如只读、存档等; 下有举例}
TDirectory.SetAttributes(); {设置文件夹属性; 下有举例}
 
//文件类
TFile.Exists();//判断指定的文件是否存在
TFile.Copy();//复制文件
TFile.Move();//移动文件
TFile.Delete();//删除文件
TFile.Replace();//替换文件
 
MotionSensor1: TMotionSensor; 加速传感器
MotionSensor1.Sensor(AngleAccelX、AngleAccelY、AngleAccelZ)加速度
procedure TAccelerometerForm.Timer1Timer(Sender: TObject);
var
  LProp: TCustomMotionSensor.TProperty;
begin
  for LProp in MotionSensor1.Sensor.AvailableProperties do
  begin
    { get the data from the sensor }
    case LProp of
      TCustomMotionSensor.TProperty.AccelerationX:
      begin
        lbAccelerationX.Visible := True;
        lbAccelerationX.Text := Format('Acceleration X: %6.2f', [MotionSensor1.Sensor.AccelerationX]);
      end;
  end;
end;
OrientationSensor1: TOrientationSensor;方位传感器
OrientationSensor1.Sensor(TiltX,TiltY,TiltZ)
procedure TOrientationSensorForm.Timer1Timer(Sender: TObject);
begin
  { get the data from the sensor }
  lbTiltX.Text := Format('Tilt X: %f', [OrientationSensor1.Sensor.TiltX]);
  lbTiltY.Text := Format('Tilt Y: %f', [OrientationSensor1.Sensor.TiltY]);
  lbTiltZ.Text := Format('Tilt Z: %f', [OrientationSensor1.Sensor.TiltZ]);
  lbHeadingX.Text := Format('Heading X: %f', [OrientationSensor1.Sensor.HeadingX]);
  lbHeadingY.Text := Format('Heading Y: %f', [OrientationSensor1.Sensor.HeadingY]);
  lbHeadingZ.Text := Format('Heading Z: %f', [OrientationSensor1.Sensor.HeadingZ]);
end;
 
TSensorManager传感器管理器(包含上述两种传感器,Samples\Object Pascal\Mobile Samples\Device Sensors and Services\SensorInfo)
TSensorCategory = (Location, Environmental, Motion, Orientation, Mechanical, Electrical, Biometric, Light, Scanner);
位置传感器,环境传感器,运动传感器,方向传感器,机械传感器,电传感器,生物传感器,光繁传感器,扫描仪传感器
 
TActionList组件可以添加标准事件(New Standard Action)
TakePhotoFromCameraAction1: TTakePhotoFromCameraAction; // 通过手机摄像头获取图片
TakePhotoFromLibraryAction1: TTakePhotoFromLibraryAction; //获取手机已存在图片
ShowShareSheetAction1: TShowShareSheetAction;//用其它程序分享图片(Bitmap.Assign();)
 
 
获取麦克风设置  FMX.Media
FMicrophone: TAudioCaptureDevice;
FMicrophone := TCaptureDeviceManager.Current.DefaultAudioCaptureDevice;
FMicrophone.FileName 设置路径
FMicrophone.State = TCaptureDeviceState.Capturing 设备状态
FMicrophone.StartCapture; //开始录音
FMicrophone.StopCapture; // 结束录音
 
MediaPlayer: TMediaPlayer; 媒体播放器
MediaPlayer.FileName 设置路径
MediaPlayer.Play; // 开始播放
MediaPlayer.Stop; // 结束播放
 
获取手机摄像头
Camera: TCameraComponent;
Camera.Active := True; //打开
Camera.Active := False; //停止
Camera.SampleBufferToBitmap(imgCameraView.Bitmap, True); //保存图片
TThread.Synchronize(TThread.CurrentThread, GetImage); //线程保存图片
Camera.Quality 图像质量
Camera.HasFlash 是否有闪光灯
Camera.TorchMode := TTorchMode.ModeOn; //打开闪光灯 Camera.FlashMode := FMX.Media.TFlashMode.fmFlashOff;
Camera.TorchMode := TTorchMode.ModeOff;//关闭闪光灯 Camera.FlashMode := FMX.Media.TFlashMode.fmFlashOn;
Camera.Kind := FMX.Media.TCameraKind.ckFrontCamera;//前置摄像头
Camera.Kind := FMX.Media.TCameraKind.ckBackCamera;//后置摄像头
 
获取设备信息
lbDeviceType.Text := Format('Device Type: %s', [JStringToString(TJBuild.JavaClass.MODEL)]);
lbOSName.Text := Format('OS Name: %s', [GetCodename(JStringToString(TJBuild_VERSION.JavaClass.RELEASE))]);
lbOSVersion.Text := Format('OS Version: %s', [JStringToString(TJBuild_VERSION.JavaClass.RELEASE)]);
 
GestureManager1: TGestureManager; 手势识别组件(igiRotate|旋转、igiZoom|缩放、igiLongTap|长按)
组件关联GestureManager1(Touch.GestureManager,Getures.Standard可以直接添加事件)
procedure TPinchZoom.FormGesture(Sender: TObject;  const EventInfo: TGestureEventInfo; var Handled: Boolean);
var
  LObj: IControl;
  LImage: TImage;
  LImageCenter: TPointF;
begin
  if EventInfo.GestureID = igiZoom then
  begin
    LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
    if LObj is TImage then
    begin
      if (not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags)) and
        (not(TInteractiveGestureFlag.gfEnd in EventInfo.Flags)) then
      begin
        { zoom the image }
        LImage := TImage(LObj.GetObject);
        LImageCenter := LImage.Position.Point + PointF(LImage.Width / 2,
          LImage.Height / 2);
        LImage.Width := LImage.Width + (EventInfo.Distance - FLastDistance);
        LImage.Height := LImage.Height + (EventInfo.Distance - FLastDistance);
        LImage.Position.X := LImageCenter.X - LImage.Width / 2;
        LImage.Position.Y := LImageCenter.Y - LImage.Height / 2;
      end;
      FLastDistance := EventInfo.Distance;
    end;
  end;
end;
 
获取地理信息
LocationSensor1: TLocationSensor;//定位
LocationSensor1.Active := swLocationSensorActive.IsChecked; //开始
NewLocation.Latitude //经度
NewLocation.Longitude //纬度
FGeocoder: TGeocoder;//地理编码
procedure TLocationForm.LocationSensor1LocationChanged(Sender: TObject;
  const OldLocation, NewLocation: TLocationCoord2D);
const
  LGoogleMapsURL: String = 'https://maps.google.com/maps?q=%s,%s';
var
  ENUSLat, ENUSLong: String; // holders for URL strings
begin
  ENUSLat := NewLocation.Latitude.ToString(ffGeneral, 5, 2, TFormatSettings.Create('en-US'));
  ENUSLong := NewLocation.Longitude.ToString(ffGeneral, 5, 2, TFormatSettings.Create('en-US'));
  { convert the location to latitude and longitude }
  lbLatitude.Text := 'Latitude: ' + ENUSLat;
  lbLongitude.Text := 'Longitude: ' + ENUSLong;
 
  { and track the location via Google Maps }
  WebBrowser1.Navigate(Format(LGoogleMapsURL, [ENUSLat, ENUSLong]));
 
  // Setup an instance of TGeocoder
  try
    if not Assigned(FGeocoder) then
    begin
      if Assigned(TGeocoder.Current) then
        FGeocoder := TGeocoder.Current.Create;
      if Assigned(FGeocoder) then
        FGeocoder.OnGeocodeReverse := OnGeocodeReverseEvent;
    end;
  except
    ListBoxGroupHeader1.Text := 'Geocoder service error.';
  end;
  // Translate location to address
  if Assigned(FGeocoder) and not FGeocoder.Geocoding then
    FGeocoder.GeocodeReverse(NewLocation);
end;
//地理信息
procedure TLocationForm.OnGeocodeReverseEvent(const Address: TCivicAddress);
begin
  ListBoxItemAdminArea.ItemData.Detail      := Address.AdminArea; //省份
  ListBoxItemCountryCode.ItemData.Detail    := Address.CountryCode; //国家编码 CN
  ListBoxItemCountryName.ItemData.Detail    := Address.CountryName; //国家
  ListBoxItemFeatureName.ItemData.Detail    := Address.FeatureName; //镇
  ListBoxItemLocality.ItemData.Detail        := Address.Locality; //市
  ListBoxItemPostalCode.ItemData.Detail      := Address.PostalCode; //邮政编码
  ListBoxItemSubAdminArea.ItemData.Detail    := Address.SubAdminArea;//子级省
  ListBoxItemSubLocality.ItemData.Detail    := Address.SubLocality;//子级市
  ListBoxItemSubThoroughfare.ItemData.Detail := Address.SubThoroughfare;//街道
  ListBoxItemThoroughfare.ItemData.Detail    := Address.Thoroughfare;//子街道
end;
 
 
获取本机信息
  FMX.Android.DeviceInfo.GetInformation;
  Memo1.Lines.Add('ID:'+FMX.Android.DeviceInfo.ID);
  Memo1.Lines.Add('IMEI:'+FMX.Android.DeviceInfo.IMEI);
  Memo1.Lines.Add('User:'+FMX.Android.DeviceInfo.User);
  Memo1.Lines.Add('Host:'+FMX.Android.DeviceInfo.Host);
  Memo1.Lines.Add('Tags:'+FMX.Android.DeviceInfo.Tags);
  Memo1.Lines.Add('Time:'+FMX.Android.DeviceInfo.Time);
  Memo1.Lines.Add('AType:'+FMX.Android.DeviceInfo.AType);
  Memo1.Lines.Add('Board:'+FMX.Android.DeviceInfo.Board);
  Memo1.Lines.Add('Radio:'+FMX.Android.DeviceInfo.Radio);
  Memo1.Lines.Add('Brand:'+FMX.Android.DeviceInfo.Brand);
  Memo1.Lines.Add('Model:'+FMX.Android.DeviceInfo.Model);
  Memo1.Lines.Add('Serial:'+FMX.Android.DeviceInfo.Serial);
  Memo1.Lines.Add('Device:'+FMX.Android.DeviceInfo.Device);
  Memo1.Lines.Add('CpuABI:'+FMX.Android.DeviceInfo.CpuABI);
  Memo1.Lines.Add('CpuABI2:'+FMX.Android.DeviceInfo.CpuABI2);
  Memo1.Lines.Add('Display:'+FMX.Android.DeviceInfo.Display);
  Memo1.Lines.Add('Product:'+FMX.Android.DeviceInfo.Product);
  Memo1.Lines.Add('Hardware:'+FMX.Android.DeviceInfo.Hardware);
  Memo1.Lines.Add('Bootloader:'+FMX.Android.DeviceInfo.Bootloader);
  Memo1.Lines.Add('FingerPrint:'+FMX.Android.DeviceInfo.FingerPrint);
  Memo1.Lines.Add('Manufacturer:'+FMX.Android.DeviceInfo.Manufacturer);
 
MapView1: TMapView;//地图足迹
 
WebBrowser1: TWebBrowser; //浏览器
WebBrowser1.Navigate('www.baidu.com'); //打开网页
WebBrowser1.URL := '';//打开网页
WebBrowser1.GoForward; //前进
WebBrowser1.GoBack;//后退
 
ShowMessage、MessageDlg、InputQuery //对话框很方便
 
消息提醒(从手机屏幕顶部向下滑动,出现的提示消息)
NotificationC: TNotificationCenter;
procedure TNotificationsForm.btnSendNotificationImmediatelyClick(
  Sender: TObject);
var
  Notification: TNotification;
begin
  { verify if the service is actually supported }
  if NotificationC.Supported then
  begin
    Notification := NotificationC.CreateNotification;
    try
      Notification.Name := 'MyNotification';
      Notification.AlertBody := 'Delphi for Mobile is here!';
      Notification.FireDate := Now; //可修改发送消息时间
 
      { Send notification in Notification Center }
      NotificationC.ScheduleNotification(Notification);
      { also this method is equivalent }
      // NotificationService.PresentNotification(Notification);
    finally
      Notification.DisposeOf;
    end;
  end
end;
  if NotificationC.Supported then
    NotificationC.CancelNotification('MyNotification'); //取消消息
    NotificationC.CancelAll; //取消所有消息
 
 
程序事件服务
var
  FMXApplicationEventService: IFMXApplicationEventService;
begin
  if TPlatformServices.Current.SupportsPlatformService (IFMXApplicationEventService, IInterface(FMXApplicationEventService)) then
    FMXApplicationEventService.SetApplicationEventHandler(HandleAppEvent)
  else
    flag := false;
end;
function TForm1.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject)  : boolean;
begin
  if flag = false then
    exit;
 
  case AAppEvent of
    TApplicationEvent.aeEnteredBackground:
      begin
          //当程序后台运行了
      end;
  end;
  Result := true;
end;
 
电话信息(Call拨号)
PhoneDialerService: IFMXPhoneDialerService;
获取电话服务信息
procedure TPhoneDialerForm.btnGetCarrierInfoClick(Sender: TObject);
var
  PhoneDialerService: IFMXPhoneDialerService;
begin
  { test whether the PhoneDialer services are supported }
  if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(PhoneDialerService)) then
  begin
    { if yes, then update the labels with the retrieved information }
    CarrierNameItem.ItemData.Detail := PhoneDialerService.GetCarrier.GetCarrierName;
    CountryCodeItem.ItemData.Detail := PhoneDialerService.GetCarrier.GetIsoCountryCode;
    NetworkCodeItem.ItemData.Detail := PhoneDialerService.GetCarrier.GetMobileCountryCode;
    MobileNetworkItem.ItemData.Detail := PhoneDialerService.GetCarrier.GetMobileNetwork;
  end
  else
    ShowMessage('PhoneDialer service not supported');
end;
 
拨号
procedure TPhoneDialerForm.btnMakeCallClick(Sender: TObject);
var
  PhoneDialerService: IFMXPhoneDialerService;
begin
  { test whether the PhoneDialer services are supported }
  if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(PhoneDialerService)) then
  begin
    { if the Telephone Number is entered in the edit box then make the call, else
      display an error message }
    if edtTelephoneNumber.Text <> '' then
      PhoneDialerService.Call(edtTelephoneNumber.Text)
    else
    begin
      ShowMessage('Please type in a telephone number.');
      edtTelephoneNumber.SetFocus;
    end;
  end
  else
    ShowMessage('PhoneDialer service not supported');
end;
 
Intent :TJIntent
uses
  Androidapi.JNI.GraphicsContentViewText, FMX.Helpers.Android, Androidapi.JNI.Net, Androidapi.Helpers;
procedureCall_URI(constAAction : JString;constAURI: string);
var
  uri: Jnet_Uri;
  Intent: JIntent;
begin
  uri := StrToJURI(AURI);
  Intent := TJIntent.JavaClass.init(AAction, uri);
  {Intent.putExtra()
//短信
Call_URI(TJIntent.JavaClass.ACTION_SENDTO, 'smsto:137114553XX');
Intent.putExtra(StringToJString('sms_body'), StringToJString('测试短信'));
  如果是要发短信等复杂的应用,需要传递各种其他的参数.要用到Intent.putExtra()传递多个参数.
  这里只封装最简单的,具体Intent.putExtra()的用法,可以查询Java的资料.大把的
  }
  SharedActivityContext.startActivity(Intent);
end;

//使用例子:
//打电话
Call_URI(TJIntent.JavaClass.ACTION_CALL, 'tel:137114553XX');
//打开地图显示某个坐标点
Call_URI(TJIntent.JavaClass.ACTION_VIEW, 'geo:38.899533,-77.036476');
//打开网页
Call_URI(TJIntent.JavaClass.ACTION_VIEW, 'www.baidu.com');
//发送电子邮件
Call_URI(TJIntent.JavaClass.ACTION_SENDTO, 'mailto:wr960204@126.com');
//播放音乐
Call_URI(TJIntent.JavaClass.ACTION_VIEW, 'file:///sdcard/download/最炫民族风.mp3');
回到主画面
procedure TForm1.Button3Click(Sender: TObject);
var
  Intent: JIntent;
begin
  Intent:= TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_MAIN);
  Intent.addCategory(TJIntent.JavaClass.CATEGORY_HOME);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  MainActivity.startActivity(Intent);
end;
 
条码扫描(需要安装zxing)
procedure TINVMCForm.btnSCANClick(Sender: TObject);
var
  uri: Jnet_Uri;      //引用Androidapi.JNI.Net
  Intent: JIntent;    //引用Androidapi.JNI.GraphicsContentViewText
  jstr:JString;
begin
  inherited;
  uri := StrToJURI('com.google.zxing.client.android.SCAN');  //引用FMX.Helpers.Android
 
  //Intent := TJIntent.JavaClass.init(jstring(('com.google.zxing.client.android.SCAN');
  Intent := TJIntent.JavaClass.init(StringToJString('com.google.zxing.client.android.SCAN'));
  SharedActivityContext.startActivity(Intent);
end;
 
 
function GetZXingIntent: JIntent;
const
  GOOGLE_ZXING = 'com.google.zxing.client.android.SCAN';
  GOOGLE_ZXING_PACKAGE = 'com.google.zxing.client.android';
begin
  Result := TJIntent.JavaClass.init(StringToJString(GOOGLE_ZXING));
  Result.setPackage(StringToJString(GOOGLE_ZXING_PACKAGE));
end;
//是否存在对应
function IsIntentCallable(const AIntent: JIntent): Boolean;
var
  LJPackageManager: JPackageManager;
begin
  Result := False;
  if Assigned(AIntent) then
  begin
    LJPackageManager := SharedActivityContext.getPackageManager;
    Result := LJPackageManager.queryIntentActivities(AIntent,
      TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size <> 0;
  end;
end;
 
获取手机信息
function GetPhoneInfo(): string;
Var
  TelephonyManager: JTelephonyManager;
  TelephonyServiceNative: JObject;
begin
  result := '';
  TelephonyServiceNative := SharedActivityContext.getSystemService
    (TJContext.JavaClass.TELEPHONY_SERVICE);
  if Assigned(TelephonyServiceNative) then
    TelephonyManager := TJTelephonyManager.Wrap
      ((TelephonyServiceNative as ILocalObject).GetObjectID);
  result := JStringToString(TelephonyManager.getLine1Number);//取得手机号
  //TelephonyManager.getDeviceId 取IMEI
  //TelephonyManager.getLine1Number 取MSISDN  手机号,大部分SIM卡中不会写入这个信息
  //TelephonyManager.getSimSerialNumber 取ICCID
  //TelephonyManager.getSubscriberId 取IMSI  运营商实际上是用这个查询的
end;
 
手机振动
uses  FMX.Helpers.Android,  Androidapi.JNI.App,  Androidapi.JNI.Os,  Androidapi.JNIBridge, FMX.StdCtrls;
procedure TForm1.Button2Click(Sender: TObject);
function GetVibratorArray(const AintArr:array of int64):TJavaArray<int64>;//震动规律函数
var
  Lindex:integer;
begin
  Result:=TJavaArray<int64>.Create(Length(AintArr));
  for Lindex:=Low(AintArr) to High(AintArr) do
      Result.Items [Lindex]:= AintArr[Lindex];
end;
var
  LVibrator:JVibrator;
  LJavaArray:TJavaArray<int64>;
begin
  LVibrator:=TJVibrator.Wrap((SharedActivity.getSystemService(TJActivity.javaClass.VIBRATOR_SERVICE ) as iLocalObject).GetObjectID );//引用震动
 
  if not LVibrator.hasVibrator then
  begin
    showmessage('手机不支持震动');
    exit;
  end;
 
  LVibrator.vibrate(200);//震动200ms
  LVibrator.cancel ;//立刻停止震动
  LJavaArray:=GetVibratorArray([200,1000,3000,5000]);//调用震动规律
  LVibrator.vibrate(LJavaArray,-1);//不重复,  震动一 次
  LJavaArray:=GetVibratorArray([200,1000,3000,5000]);//调用震动规律
  LVibrator.vibrate(LJavaArray,0);//v不停重复,大于0的参数,可以指定震动次数
end;
 
 
网络传送文件(类似Server/Client)
TTetheringManager|设备管理、TTetheringAppProfile|文件发送
 
蓝牙
System.Bluetooth单元中主要包含一下几个类
TBluetoothManager、TBluetoothDeviceList、TBluetoothAdapter、TBluetoothDevice、TBluetoothService、
TBluetoothServiceList、TBluetoothSocket
TBluetoothManager是蓝牙管理器,用于蓝牙设备管理,包括发现蓝牙设备,获取配对设备,处理远程配对请求等功能
TBluetoothDeviceList是蓝牙设备列表,TBluetoothDeviceList = class(TObjectList<TBluetoothDevice>),可以通过TBluetoothManager.GetPairedDevices获得配对设备列表
TBluetoothAdapter本机蓝牙设备,实现配对、取消配对等功能,可通过TBluetoothManager.CurrentAdapter得到当前蓝牙设备
TBluetoothDevice远端蓝牙设备,每个远端设备可以提供若干个服务(TBluetoothService),
TBluetoothService远端蓝牙设备服务,包括服务名和UUID
TBluetoothServiceList服务列表 = class(TList<TBluetoothService>);可通过TBluetoothDevice.GetServices获得远端设备服务列表
TBluetoothSocket蓝牙通讯套接字,通过 TBluetoothDevice.CreateClientSocket(StringToGUID(ServiceGUI), True/False)创建
 
TimeEdit1: TTimeEdit;//时间选择
HorzScrollBox1: THorzScrollBox;横拉组件
MultiView1: TMultiView;//多余视图(Mode主明细表,可更改弹出方式)
EMSProvider: TEMSProvider;//企业移动服务
BBAS Client(组件组TKinveyProvider、TParseProvider);移动客户端数据连接组件
TabItem1: TTabItem;//多页
 
退出键不退出程序
procedure TPForm.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char;
  Shift: TShiftState);
begin
  if Key = vkHardwareBack then
  begin
 
    {$IFDEF ANDROID}
    MessageDlg('确认退出吗?', System.UITypes.TMsgDlgType.mtInformation,
    [
      System.UITypes.TMsgDlgBtn.mbYes,
      //System.UITypes.TMsgDlgBtn.mbNo,
      System.UITypes.TMsgDlgBtn.mbCancel
    ], 0, System.UITypes.TMsgDlgBtn.mbCancel,
    procedure(const AResult: TModalResult)
    begin
      if AResult = mrYES then
        MainActivity.finish; { 退出程序 }  // use FMX.Platform.Android
      end);
    {$ENDIF ANDROID}
    //close;
    Key := 0;
    exit;
  end;
end;
 
 
 
Application.FormFactor.Orientations := [TFormOrientation.Landscape]; //坚屏
Application.FormFactor.Orientations := [TFormOrientation.Portrait];//横屏
 
当前网络状态(Androidapi.JNI.Network.pas)
IsConnected|连接,IsWiFiConnected|Wifi是否连接,IsMobileConnected|移动网络是否连接
 
剪贴版FClipboardService: IFMXClipboardService;
TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService,    IInterface(FClipboardService));
FClipboardService.SetClipboard(Tvalue(Edit1.Text));  //复制
FClipboardService.GetClipboard.ToString;  //粘贴
 
键盘FService: IFMXVirtualKeyboardToolbarService;
if TPlatformServices.Current.SupportsPlatformService (IFMXVirtualKeyboardToolbarService, IInterface(FService)) then
begin
  FService.SetToolbarEnabled(true);
  FService.SetHideKeyboardButtonVisibility(true);
end;
 
添加桌面快捷方式
procedure Tform1.Button1Click(Sender: TObject);
{$IFDEF ANDROID}
var
  ShortcutIntent: JIntent;
  addIntent: JIntent;
  wIconIdentifier : integer;
  wIconResource : JIntent_ShortcutIconResource;
{$ENDIF}
begin
{$IFDEF ANDROID}
 
  ShortcutIntent := TJIntent.JavaClass.init(SharedActivityContext, SharedActivityContext.getClass);
  ShortcutIntent.setAction(TJIntent.JavaClass.ACTION_MAIN);
 
  addIntent := TJIntent.Create;
  addIntent.putExtra(TJIntent.JavaClass.EXTRA_SHORTCUT_INTENT, TJParcelable.Wrap((shortcutIntent as ILocalObject).GetObjectID));
  addIntent.putExtra(TJIntent.JavaClass.EXTRA_SHORTCUT_NAME, StringToJString(Application.Title));
  addIntent.setAction(StringToJString('com.android.launcher.action.INSTALL_SHORTCUT'));
  // get icon resource identifier
  wIconIdentifier := SharedActivity.getResources.getIdentifier(StringToJString('ic_launcher'), StringToJString('drawable'), StringToJString('com.embarcadero.Project1'));
  wIconResource := TJIntent_ShortcutIconResource.JavaClass.fromContext(SharedActivityContext, wIconIdentifier);
  // set icon for shortcut
  addIntent.putExtra(TJIntent.JavaClass.EXTRA_SHORTCUT_ICON_RESOURCE, TJParcelable.Wrap((wIconResource as ILocalObject).GetObjectID));
 
  SharedActivityContext.sendBroadcast(addIntent);
{$ENDIF}
end;
 
截取屏幕图片
function MakeScaleScreenshot(Sender: TControl): TBitmap;
  function GetScreenScale: Single;
  var
    ScreenService: IFMXScreenService;
  begin
    Result := 1;
    if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService,  IInterface(ScreenService)) then
    begin
      Result := ScreenService.GetScreenScale;
    end;
  end;
var
  fScreenScale: Single;
begin
  fScreenScale := GetScreenScale;
  Result := TBitmap.Create(Round(Sender.Width * fScreenScale),
    Round(Sender.Height * fScreenScale));
  Result.Clear(0);
  if Result.Canvas.BeginScene then
    try
      Sender.PaintTo(Result.Canvas, RectF(0, 0, Result.Width, Result.Height));
    finally
      Result.Canvas.EndScene;
    end;
end;

判断屏幕是否关闭
uses Androidapi.JNI.android.os.PowerManager,
     Androidapi.JNI.GraphicsContentViewText,
     Androidapi.JNI.JavaTypes,
     Androidapi.Helpers,
     Androidapi.JNIBridge;
{$R *.NmXhdpiPh.fmx ANDROID}

function GetPowerManager:JPowerManager ;
var
 Native:JObject ;
begin
 Native:=SharedActivityContext.getSystemService(TJContext.JavaClass.POWER_SERVICE);
 if not Assigned(Native) then
 begin
   raise Exception.Create('Could not locate Connectivity Service');
 end;
 Result:=TJPowerManager.Wrap((Native as ILocalObject).GetObjectID) ;
 if not Assigned(Result) then
 begin
   raise Exception.Create('Could not access Connectivity Manager');
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 PowerManager:JPowerManager ;
begin
 PowerManager:=GetPowerManager ;
 if PowerManager.isScreenOn then
 begin
   ShowMessage('未关闭状态');
 end
 else
 begin
   ShowMessage('已经关闭状态');
 end;
end;

// 获取版本号
利用IFMXApplicationService接口,访问AppVersion即可取得应用的版本号。
代码如下,Win32,Android测试通过,需要引用FMX.Platform单元。
 
function GetAppVersion: String;
var
  ApplicationService: IFMXApplicationService;
begin
  result := '';
 
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationService,
    ApplicationService) then
  begin
 
      result := ApplicationService.AppVersion;
 
  end;
end;
 
还记得有朋友针对每个平台做过实现,很烦麻,利用这个就简化多了,同时,是Xe8自带的。
 
后记:
Delphi xe8 update1,Delphi 10对于win32平台,只取出前两位做为版本号,如1.0.0.0,则取出为1.0,原因在这里:
单元FMX.PlatForm.Win:
 
function TPlatformWin.GetVersionString: string;
const
  UndefinedVersionInfo = Cardinal(-1);
var
  VersionInfo: Cardinal;
begin
  VersionInfo := GetFileVersion(ParamStr(0));
  if VersionInfo <> UndefinedVersionInfo then
    Result := Format('%d.%d', [HiWord(VersionInfo), LoWord(VersionInfo)])
  else
    Result := string.Empty;
end;

// 手动 关闭/显示 虚拟键盘  uses    FMX.VirtualKeyboard, FMX.Platform
var
  kbd:IFMXVirtualKeyboardService;
begin
  kbd:=TPlatformServices.Current.GetPlatformService(IFMXVirtualKeyboardService) as IFMXVirtualKeyboardService;
  kbd.ShowVirtualKeyboard(edPwd);        //显示虚拟键盘
  //kbd.HideVirtualKeyboard;        //隐藏虚拟键盘
end

// apk 加固  (马花藤家免费加固)  加固后  得用 jarsigner重新签名,

// 判断当前窗体状态
创建时 注册
procedure TMainForm.FormCreate(Sender: TObject);
var
  SvcEvents: IFMXApplicationEventService;
begin
  if TPlatformServices.Current.SupportsPlatformService
    (IFMXApplicationEventService, IInterface(SvcEvents))
  then
    SvcEvents.SetApplicationEventHandler(HandleAppEvent);
end;
 
// 事件
function TMainForm.HandleAppEvent(AAppEvent: TApplicationEvent;
  AContext: TObject): Boolean;
var
  fState: string;
begin
 
  //
  case AAppEvent of
    TApplicationEvent.FinishedLaunching:
      fState := 'FinishedLaunching';      //完成 开始?
    TApplicationEvent.BecameActive:
      fState := 'BecameActive';            // 激活(后台切换过来,或者点亮屏幕,都会触发)
    TApplicationEvent.WillBecomeInactive:
      fState := 'WillBecomeInactive';      // 将变成后台运行
    TApplicationEvent.EnteredBackground:
      fState := 'EnteredBackground';        // 输入背景(弹出后台菜单时)
    TApplicationEvent.WillBecomeForeground:
      fState := 'WillBecomeForeground';    // 将转为前景
    TApplicationEvent.WillTerminate:
      fState := 'WillTerminate';          // 将终止
    TApplicationEvent.LowMemory:
      fState := 'LowMemory';              // 内存不足
    TApplicationEvent.TimeChange:
      fState := 'TimeChange';            // 时间变化
    TApplicationEvent.OpenURL:
      fState := 'OpenURL';                //打开网址
  end;
 
  // 激活窗体时 重连数据
  if (fState = 'BecameActive') and (_DataConnect.FConnected) then
  begin
    //ShowHintFrame( self, fState );
    _DataConnect.TestConnect;
  end;
 
  Result := true;
 
end;

// Edit 只允许输入数值
1. 设置编辑框的输入格式  FilterChar = 0123456789. 
 
2. 处理多次输入 小数点 的情况
procedure TForm1.SkinFMXEdit1Validating(Sender: TObject; var Text: string);
var
  aText:String;
begin
  //
  aText := LeftStr(Text,length(Text)-1);
  if (pos('.',aText) > 0 ) and (Text[length(Text)]='.') then
  begin
    Text :=  aText;
  end;
 
end;

//动态释放 TFDJSONDataSets;

// 调用摄像头 

FMX VCL TAlphaColor TColor 互转 
function ColorToAlphaColor(AColor: TColor; Alpha:Byte = $FF): TAlphaColor;
begin
  TAlphaColorRec(Result).A :=  Alpha;
  TAlphaColorRec(Result).R := TColorRec(AColor).R;
  TAlphaColorRec(Result).G := TColorRec(AColor).G;
  TAlphaColorRec(Result).B := TColorRec(AColor).B;
end;
 
function AlphaColorToColor(AAlpahColor: TAlphaColor; IgnoreAlpha: Boolean = True): TColor;
begin
  if IgnoreAlpha then
  begin
    TColorRec(Result).R := TAlphaColorRec(AAlpahColor).R;
    TColorRec(Result).G := TAlphaColorRec(AAlpahColor).G;
    TColorRec(Result).B := TAlphaColorRec(AAlpahColor).B;
  end
  else
  begin
    TColorRec(Result).R := TAlphaColorRec(AAlpahColor).R * TAlphaColorRec(AAlpahColor).A div $FF;
    TColorRec(Result).G := TAlphaColorRec(AAlpahColor).G * TAlphaColorRec(AAlpahColor).A div $FF;
    TColorRec(Result).B := TAlphaColorRec(AAlpahColor).B * TAlphaColorRec(AAlpahColor).A div $FF;
  end;
end;
 
 
 

Android 沉浸式状态栏(透明状态栏)  和导航栏方法
<resources xmlns:android="http://schemas.android.com/apk/res/android"> 
<style name="AppTheme" parent="@android:style/Theme.NoTitleBar">
    <item name="android:windowBackground">@drawable/splash_image</item>  
  <item name="android:windowNoTitle">true</item>  
  <item name="android:windowFullscreen">true</item>  
  <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowTranslucentNavigation">false</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
修改编译后的 Style.xml 文件 添加内容
<item name="android:windowTranslucentStatus">true</item>  // 状态栏值 为True 代表透明 
<item name="android:windowTranslucentNavigation">false</item>  // 虚拟导航栏 Ture=透明

// 使用TrueType 字库做图标 iconfont 
1.修改Delphi 源码 FMX.FontGlyphs.Android.pas。(文件位于 C:\Program Files (x86)\Embarcadero\Studio\15.0\source\fmx)
 

A. 添加对 System.IOUtils to 的引用到 uses 部分。

B. 找到 procedure TAndroidFontGlyphManager.LoadResource;

  • 添加一个变量 FontFile: string;
  • 在过程中有一行是:Typeface := TJTypeface.JavaClass.create(FamilyName, TypefaceFlag);
  • 将上面的内容替换为:   
    FontFile := TPath.GetDocumentsPath + PathDelim + CurrentSettings.Family + '.ttf';
    if FileExists(FontFile) then
       Typeface := TJTypeface.JavaClass.createFromFile(StringToJString(FontFile))
    else
       Typeface := TJTypeface.JavaClass.Create(FamilyName, TypefaceFlag);
     
 
2.制作ttf图标字库
来到www.iconfont.cn,注册一个用户,这是必须的
然后,你可以选择自己要用的图标,加入购物车。最好从一组图标中选择,保持风格一致。
选择好所有的图标后,进入购物车,在这里,你可以建立一个项目,把选择的图标加入到你建立的项目中。
选择你的项目,下载到本地,你会得到一个download.zip打开他,会看到里面的iconfont.tff文档,这就是我们需要的TrueType字库。
把iconfont.tff拖放到c:\windows\fonts,在windows系统中安装他。
3. 将 iconfont.tff 添加到 Depolyment 中, 设置远程路径为 .\assets\internal\
 
4. 图标使用方法
    设置  TextSettings 的字体家族(FontFamily)属性为 iconfont
    运行期间给Text 赋值, 如: lable1.Text := #$e624;  // 注意字体图标代码原型是 &#xe624; 在Delphi中要将 &#x 要改为 #$ + 后面代码e624
  动态转换:  char(StrToInt('$'+AItem.Detail5))
 
用于IOS 的话要修改配置文件 info.plist.TemplateOS.xml,把字体文件名加进去
<key>UIAppFonts</key>
<array>
<string>iconfont.ttf</string>
</array>
Delphi <wbr>FMX <wbr>开发APP使用TrueType字库做图标
 

截取指定字符串  读取网址的最后文件名如:  https://static.pexels.com/photos/792/wood-landscape-mountains-nature.jpg
FFileName := Edit1.Text.Substring(Edit1.Text.LastIndexOf('/') +1);  // 结果为 wood-landscape-mountains-nature.jpg

// 调用相机拍照 图片不清晰, 如何保持原图质量?
1. 拍照或调用相册前,设置图片最大尺寸
// 初始化图片尺寸, 拍照 / 相册
ActionTakePhotoFromCamera.MaxHeight := 102400;
ActionTakePhotoFromCamera.MaxWidth := 102400;
ActionTakePhotoFromLibrary.MaxHeight := 102400;
ActionTakePhotoFromLibrary.MaxWidth := 102400;
 
 
 
 
 
 
 
 
 
 
 
1
// 初始化图片尺寸, 拍照 / 相册
2
ActionTakePhotoFromCamera.MaxHeight := 102400;
3
ActionTakePhotoFromCamera.MaxWidth := 102400;
4
ActionTakePhotoFromLibrary.MaxHeight := 102400;
5
ActionTakePhotoFromLibrary.MaxWidth := 102400;
 
 
2. Image保存图片时要设置质量参数
var
  Quality:TBitmapCodecSaveParams; // 图片保存
  ...
  quality1.Quality:=100; //100% 图片质量
  Image1.Bitmap.SaveToFile( System.IOUtils.tPath.getsharedcamerapath+'/temp.jpg', @quality1 );
 
 
 
 
 
 
 
 
 
 
 
1
var
2
  Quality:TBitmapCodecSaveParams; // 图片保存
3
  ...
4
  quality1.Quality:=100; //100% 图片质量
5
  Image1.Bitmap.SaveToFile( System.IOUtils.tPath.getsharedcamerapath+'/temp.jpg', @quality1 );
 
 

Delphi跨平台下的GetTickCount,GetCurrentThreadID
在Windows下只要uses Windows,就有这两个API可调用GetTickCount,GetCurrentThreadID
如果我们需要跨平台使用这两个函数,就不能仅仅Uses Windows了。
如果需要跨平台使用GetTickCount,可以uses System.Classes,然后使用类方法:TThread.GetTickCount
如果需要跨平台使用GetCurrentThreadID,则仅需引用不同的单元即可:
uses 
{$ifdef MSWINDOWS}
Windows;
{$endif}
{$ifdef POSIX}
Posix.Pthread;
{$endif}

全面屏下方出无法填满
建议开发者在自己App AndroidManifest的Application标签下面增加下面一段代码:
<meta-data android:name="android.max_aspect" android:value="2.1" />

// Android 播放声音
调用安卓原生类播放:
JRingtoneManager 类播放
 
 
 
 
 
 
 
 
1
var
2
  fRing: JRingtoneManager;
3
  uri: Jnet_Uri;
4
// TYPE_RINGTONE 电话铃
5
  // TYPE_NOTIFICATION 提示音(响一下)
6
  // TYPE_ALARM 闹钟音
7
  //uri := StrToJURI('file://' + IOUtils.TPath.GetDocumentsPath+PathDelim+'beep.wav');
8

9
  // 获取系统默认音频位置
10
  uri := TJRingtoneManager.JavaClass.getDefaultUri(
11
    TJRingtoneManager.JavaClass.TYPE_NOTIFICATION
12
  );
13
  //加载
14
  if uri <> nil then
15
    fRing := TJRingtoneManager.JavaClass.getRingtone( TAndroidHelper.Activity, uri);
16
  // 播放
17
  fRing.Play;
 
 
// JSoundPool 类播放短音频文件  (占用资源少,推荐提示音调用)
// 实例化 {第一个参数为soundPool可以支持的声音数量,这决定了Android为其开设多大的缓冲区, 第二个参数声音类型, 第三个参数声音质量,越高占用资源也越多}
  fJSoundPool := TJSoundPool.JavaClass.init(1,TJAudioManager.JavaClass.STREAM_NOTIFICATION,5);
  // 加载音频文件 {参数:音频文件路径, 优先级}
  fJSoundPool.load( StringToJString(IOUtils.TPath.GetDocumentsPath+PathDelim+'beep.wav'), 1 );
// 播放
 fJSoundPool.play(1,1, 1, 0, 0, 1);
 //play参数:第一个参数为id,id即为放入到soundPool中的顺序,比如现在beep.wav是第一个,因此它的id就是1。
 //第二个和第三个参数为左右声道的音量控制。第四个参数为优先级,由于只有这一个声音,因此优先级在这里并不重要:优先级,值越大,优先级越高。
 //第五个参数为是否循环播放,0为不循环,-1为循环。最后一个参数为播放比率,从0.5到2,一般为1,表示正常播放。
 
 
 
 
 
 
 
 
 
 
 
1
// 实例化 {第一个参数为soundPool可以支持的声音数量,这决定了Android为其开设多大的缓冲区, 第二个参数声音类型, 第三个参数声音质量,越高占用资源也越多}
2
  fJSoundPool := TJSoundPool.JavaClass.init(1,TJAudioManager.JavaClass.STREAM_NOTIFICATION,5);
3
  // 加载音频文件 {参数:音频文件路径, 优先级}
4
  fJSoundPool.load( StringToJString(IOUtils.TPath.GetDocumentsPath+PathDelim+'beep.wav'), 1 );
5
// 播放
6
 fJSoundPool.play(1,1, 1, 0, 0, 1);
7
 //play参数:第一个参数为id,id即为放入到soundPool中的顺序,比如现在beep.wav是第一个,因此它的id就是1。
8
 //第二个和第三个参数为左右声道的音量控制。第四个参数为优先级,由于只有这一个声音,因此优先级在这里并不重要:优先级,值越大,优先级越高。
9
 //第五个参数为是否循环播放,0为不循环,-1为循环。最后一个参数为播放比率,从0.5到2,一般为1,表示正常播放。
 
 
https://blog.csdn.net/pku_android/article/details/7625868?utm_source=blogxgwz7
// TMediaPlayer控件播放
// 创建播放器...
  Speaker := TMediaPlayer.Create(Self);

3、使用如下过程播放声音:
//
// 播放声音...
procedure TMainForm.MsgArriveSound;
begin
   Speaker.FileName := IOUtils.TPath.GetDocumentsPath+PathDelim+'beep.wav';
   if Assigned(Speaker.Media) then
     Speaker.Play;
end;
 
 
 
 
 
 
 
 
 
 
 
1
// 创建播放器...
2
  Speaker := TMediaPlayer.Create(Self);
3

4
3、使用如下过程播放声音:
5
//
6
// 播放声音...
7
procedure TMainForm.MsgArriveSound;
8
begin
9
   Speaker.FileName := IOUtils.TPath.GetDocumentsPath+PathDelim+'beep.wav';
10
   if Assigned(Speaker.Media) then
11
     Speaker.Play;
12
end;
 
 
// 获取系统默认提示音
引用单元
  Androidapi.JNI.Media
  Androidapi.Helpers
  Androidapi.JNI.Net

procedure TfrmWatch.PlayAlertSound;
var
  vRingtone: JRingtone;
  vNotificationRri: Jnet_Uri;
begin
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri(TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
  vRingtone := TJRingtoneManager.JavaClass.getRingtone(TAndroidHelper.Context.getApplicationContext, vNotificationRri);
  vRingtone.play;
end;

 //提示音
if ComboBox1.Selected.Text='TYPE_ALARM'
then
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
  (TJRingtoneManager.JavaClass.TYPE_ALARM);

 //电话铃声
if ComboBox1.Selected.Text='TYPE_RINGTONE'
then
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
  (TJRingtoneManager.JavaClass.TYPE_RINGTONE);

  //消息提示音
 if ComboBox1.Selected.Text='TYPE_NOTIFICATION'
then
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
  (TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
 
 
 
 
 
 
 
 
 
 
 
1
引用单元
2
  Androidapi.JNI.Media
3
  Androidapi.Helpers
4
  Androidapi.JNI.Net
5

6
procedure TfrmWatch.PlayAlertSound;
7
var
8
  vRingtone: JRingtone;
9
  vNotificationRri: Jnet_Uri;
10
begin
11
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri(TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
12
  vRingtone := TJRingtoneManager.JavaClass.getRingtone(TAndroidHelper.Context.getApplicationContext, vNotificationRri);
13
  vRingtone.play;
14
end;
15

16
 //提示音
17
if ComboBox1.Selected.Text='TYPE_ALARM'
18
then
19
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
20
  (TJRingtoneManager.JavaClass.TYPE_ALARM);
21

22
 //电话铃声
23
if ComboBox1.Selected.Text='TYPE_RINGTONE'
24
then
25
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
26
  (TJRingtoneManager.JavaClass.TYPE_RINGTONE);
27

28
  //消息提示音
29
 if ComboBox1.Selected.Text='TYPE_NOTIFICATION'
30
then
31
  vNotificationRri := TJRingtoneManager.JavaClass.getDefaultUri
32
  (TJRingtoneManager.JavaClass.TYPE_NOTIFICATION);
 
 


// 定义 广播接收器
引用单元 uses
Androidapi.JNIBridge, Androidapi.JNI.App,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.JavaTypes, Androidapi.Helpers, Androidapi.JNI.Embarcadero

// 
type
  // 接收广播的类
	TScanBroadcastReceiver = class(TJavaLocal, JFMXBroadcastReceiverListener)
  public
    // 重写广播接收事件
    procedure onReceive(context: JContext; intent: JIntent); cdecl;
  end;
  
  // 变量
 FListener: TScanBroadcastReceiver;
 FReceiver: JFMXBroadcastReceiver;
 FFilter: JIntentFilter;
 
// 
procedure TForm2.FormCreate(Sender: TObject);
begin
  // 创建接收者
	FListener := TScanBroadcastReceiver.Create();
  if not Assigned(FListener) then
    Exit;

  FReceiver := TJFMXBroadcastReceiver.JavaClass.init(FListener);
  if not Assigned(FReceiver) then
    Exit;

	// 通知意图过滤器  RES_ACTION = 需要过滤的事件 如: 'android.intent.action.SCREEN_ON' 监听屏幕点亮事件
	FFilter := TJIntentFilter.JavaClass.init(StringToJString('RES_ACTION'));
	if not Assigned(FFilter) then
    Exit;
	FFilter.addAction(StringToJString('RES_ACTION'));
	FFilter.addCategory(StringToJString('android.intent.category.DEFAULT'));

	// 动态注册广播接受者
	SharedActivityContext.registerReceiver(FReceiver, FFilter);
end;

//释放
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  SharedActivityContext.unregisterReceiver(FReceiver);
end;
 
 
 
 
 
 
 
 
 
 
 
1
引用单元 uses
2
Androidapi.JNIBridge, Androidapi.JNI.App,
3
Androidapi.JNI.GraphicsContentViewText,
4
Androidapi.JNI.JavaTypes, Androidapi.Helpers, Androidapi.JNI.Embarcadero
5

6
// 
7
type
8
  // 接收广播的类
9
TScanBroadcastReceiver = class(TJavaLocal, JFMXBroadcastReceiverListener)
10
  public
11
    // 重写广播接收事件
12
    procedure onReceive(context: JContext; intent: JIntent); cdecl;
13
  end;
14

15
  // 变量
16
 FListener: TScanBroadcastReceiver;
17
 FReceiver: JFMXBroadcastReceiver;
18
 FFilter: JIntentFilter;
19

20
// 
21
procedure TForm2.FormCreate(Sender: TObject);
22
begin
23
  // 创建接收者
24
FListener := TScanBroadcastReceiver.Create();
25
  if not Assigned(FListener) then
26
    Exit;
27

28
  FReceiver := TJFMXBroadcastReceiver.JavaClass.init(FListener);
29
  if not Assigned(FReceiver) then
30
    Exit;
31

32
// 通知意图过滤器  RES_ACTION = 需要过滤的事件 如: 'android.intent.action.SCREEN_ON' 监听屏幕点亮事件
33
FFilter := TJIntentFilter.JavaClass.init(StringToJString('RES_ACTION'));
34
if not Assigned(FFilter) then
35
    Exit;
36
FFilter.addAction(StringToJString('RES_ACTION'));
37
FFilter.addCategory(StringToJString('android.intent.category.DEFAULT'));
38

39
// 动态注册广播接受者
40
SharedActivityContext.registerReceiver(FReceiver, FFilter);
41
end;
42

43
//释放
44
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
45
begin
46
  SharedActivityContext.unregisterReceiver(FReceiver);
47
end;
48

 
 
 
 

多线程
TThread.Synchronize();
TThread.CreateAnonymousThread().start;
TTask.Run();
//在这里不能直接访问UI,启动定时器,在定时器里面进行操作
 CallInUIThread(
          procedure
          begin
            tmrScanResult.Enabled:=True;
          end);
 
 

Android 发送邮件中文乱码问题
 IdMessage1.Clear;
  idmessage1.CharSet := 'gb2312'; // 这句不能解决乱码的问题
  IdMessage1.Encoding := meDefault;
  IdMessage1.From.address := 'xxxx@sina.com'; // 发件人地址
  IdMessage1.Recipients.EMailAddresses := 'xxxxxxxxxx'; // 收信人地址
  IdMessage1.Subject := '测试'; // 邮件标题
  IdMessage1.Priority := mphigh; //优先级
  IdMessage1.Body.Text := '这是一封测试邮件'; //邮件内容
  try
     IdSMTP1.Send(IdMessage1);
  finally
     IdSMTP1.Disconnect;
  end;
  ShowMessage('发送完毕!');
  
  // 问题解决了!只要这里改一下就行,也不用Android的代码,只用Indy的代码.
  IdMessage1.Subject := '';  // 邮件标题
  IdMessage1.ExtraHeaders.Values['Subject'] := EncodeHeader(UTF8Encode('测试'), '',  'Q', 'UTF-8');
 
 
 
 
 
 
 
 
 
 
 
1
 IdMessage1.Clear;
2
  idmessage1.CharSet := 'gb2312'; // 这句不能解决乱码的问题
3
  IdMessage1.Encoding := meDefault;
4
  IdMessage1.From.address := 'xxxx@sina.com'; // 发件人地址
5
  IdMessage1.Recipients.EMailAddresses := 'xxxxxxxxxx'; // 收信人地址
6
  IdMessage1.Subject := '测试'; // 邮件标题
7
  IdMessage1.Priority := mphigh; //优先级
8
  IdMessage1.Body.Text := '这是一封测试邮件'; //邮件内容
9
  try
10
     IdSMTP1.Send(IdMessage1);
11
  finally
12
     IdSMTP1.Disconnect;
13
  end;
14
  ShowMessage('发送完毕!');
15

16
  // 问题解决了!只要这里改一下就行,也不用Android的代码,只用Indy的代码.
17
  IdMessage1.Subject := '';  // 邮件标题
18
  IdMessage1.ExtraHeaders.Values['Subject'] := EncodeHeader(UTF8Encode('测试'), '',  'Q', 'UTF-8');
 
 

 // 解决安卓下 ProcessMesage 失效的问题
 将 Application.ProcessMessage 改为 Application.HandleMessage
 
 
 
 
 
 
 
 
 
 
 
1
 // 解决安卓下 ProcessMesage 失效的问题
2
  Application.ProcessMessage 改为 Application.HandleMessage
 
 

// 安卓  Android.Settings  引用单元: Androidapi.JNI.Provider
// 跳转 未知应用安装权限
// 需要设置权限  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
var
  intent: Jintent;
  packageURI: Jnet_Uri;
  Str: Jstring;
begin

  // 测试升级

  try
    // 判断是否有安装未知应用权限

    if SharedActivityContext.getPackageManager.canRequestPackageInstalls then
    begin
      mLog.Lines.Add('canRequestPackageInstalls = yes');
    end
    else
    begin
      // 需要开启安装未知来源权限
      mLog.Lines.Add('canRequestPackageInstalls = no');

      // 打开   安装未知来源权限
      Str := StringToJString('package:'+ JStringToString(SharedActivityContext.getPackageName) );
      packageURI := TJnet_Uri.JavaClass.parse( Str );
      intent := TJintent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);

      SharedActivityContext.startActivity(intent);

    end;

  except on E:Exception do
    mLog.Lines.Add('canRequestPackageInstalls:'+e.Message);
  end;
 
 
 
 
 
 
 
 
 
 
 
1
var
2
  intent: Jintent;
3
  packageURI: Jnet_Uri;
4
  Str: Jstring;
5
begin
6

7
  // 测试升级
8

9
  try
10
    // 判断是否有安装未知应用权限
11

12
    if SharedActivityContext.getPackageManager.canRequestPackageInstalls then
13
    begin
14
      mLog.Lines.Add('canRequestPackageInstalls = yes');
15
    end
16
    else
17
    begin
18
      // 需要开启安装未知来源权限
19
      mLog.Lines.Add('canRequestPackageInstalls = no');
20

21
      // 打开   安装未知来源权限
22
      Str := StringToJString('package:'+ JStringToString(SharedActivityContext.getPackageName) );
23
      packageURI := TJnet_Uri.JavaClass.parse( Str );
24
      intent := TJintent.JavaClass.init(TJSettings.JavaClass.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
25

26
      SharedActivityContext.startActivity(intent);
27

28
    end;
29

30
  except on E:Exception do
31
    mLog.Lines.Add('canRequestPackageInstalls:'+e.Message);
32
  end;
33

 
 
 

Android 9  http请求无效
<!-- Android9.0以上默认不不⽀支持http通信,为保证SDK正常使⽤用,请在application节点下新增该属性 -->
<application android:usesCleartextTraffic="true">


//保存图片到相册 基本就是这两个方法了
function MediaScanner2(const ImageFileName: string; const DisplayError: Boolean = False): Boolean;
var
  c: Integer;
  JMediaScannerCon: Androidapi.Jni.Media.JMediaScannerConnection;
  JMediaScannerCon_Client: Androidapi.Jni.Media.JMediaScannerConnection_MediaScannerConnectionClient;
begin
  Result := False;
  try
    JMediaScannerCon:=TJMediaScannerConnection.JavaClass.init(GetContext, JMediaScannerCon_Client);
    JMediaScannerCon.connect;
    c:=0;
    while not JMediaScannerCon.isConnected do begin
      Sleep(50);
      inc(c);
      if (c > 20) then break;
    end;
    if (JMediaScannerCon.isConnected) then begin
      JMediaScannerCon.scanFile(StringToJString(ImageFileName), nil);
      JMediaScannerCon.disconnect;
      Result := True;
    end;
  except
    on E: Exception do begin
      if DisplayError then ShowMessage('Error: ' + e.Message);

      PostLog(llError, Format('MediaScanner Error: [%s]%s', [E.ClassName, E.Message]));
    end;
  end;
end;

//https://blog.csdn.net/ChinaWallace/article/details/48547965
//https://blog.csdn.net/wolfking0608/article/details/79138716
function MediaScanner(const ImageFileName: string; const DisplayError: Boolean = False): Boolean;
var
  Intent: JIntent;
begin
  Result := False;

  // There may be an issue with the geo: prefix and URLEncode.
  // will need to research
  if TJBuild_VERSION.JavaClass.SDK_INT >= 19 then
    Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MEDIA_SCANNER_SCAN_FILE)
  else
    Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MEDIA_MOUNTED);
  try
    Intent.setData(TAndroidHelperEx.UriFromFileName(ImageFileName));
    TAndroidHelper.Activity.sendBroadcast(Intent);
    Result := True;
  except
    on E: Exception do begin
      if DisplayError then ShowMessage('Error: ' + e.Message);

      PostLog(llError, Format('MediaScanner Error: [%s]%s', [E.ClassName, E.Message]));
    end;
  end;
end;

// 这种方式 待测试   FMX.MediaLibrary, FMX.Platform,
 var
  PhotoService: IFMXPhotoLibrary;
  if TPlatformServices.Current.SupportsPlatformService(IFMXPhotoLibrary, PhotoService) then
      PhotoService.AddImageToSavedPhotosAlbum(ABitmap,DoWriteImageCompletionEvent);
  
 
 
 
 
 
 
 
 
 
 
 
1
//保存图片到相册 基本就是这两个方法了
2
function MediaScanner2(const ImageFileName: string; const DisplayError: Boolean = False): Boolean;
3
var
4
  c: Integer;
5
  JMediaScannerCon: Androidapi.Jni.Media.JMediaScannerConnection;
6
  JMediaScannerCon_Client: Androidapi.Jni.Media.JMediaScannerConnection_MediaScannerConnectionClient;
7
begin
8
  Result := False;
9
  try
10
    JMediaScannerCon:=TJMediaScannerConnection.JavaClass.init(GetContext, JMediaScannerCon_Client);
11
    JMediaScannerCon.connect;
12
    c:=0;
13
    while not JMediaScannerCon.isConnected do begin
14
      Sleep(50);
15
      inc(c);
16
      if (c > 20) then break;
17
    end;
18
    if (JMediaScannerCon.isConnected) then begin
19
      JMediaScannerCon.scanFile(StringToJString(ImageFileName), nil);
20
      JMediaScannerCon.disconnect;
21
      Result := True;
22
    end;
23
  except
24
    on E: Exception do begin
25
      if DisplayError then ShowMessage('Error: ' + e.Message);
26

27
      PostLog(llError, Format('MediaScanner Error: [%s]%s', [E.ClassName, E.Message]));
28
    end;
29
  end;
30
end;
31

32
//https://blog.csdn.net/ChinaWallace/article/details/48547965
33
//https://blog.csdn.net/wolfking0608/article/details/79138716
34
function MediaScanner(const ImageFileName: string; const DisplayError: Boolean = False): Boolean;
35
var
36
  Intent: JIntent;
37
begin
38
  Result := False;
39

40
  // There may be an issue with the geo: prefix and URLEncode.
41
  // will need to research
42
  if TJBuild_VERSION.JavaClass.SDK_INT >= 19 then
43
    Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MEDIA_SCANNER_SCAN_FILE)
44
  else
45
    Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MEDIA_MOUNTED);
46
  try
47
    Intent.setData(TAndroidHelperEx.UriFromFileName(ImageFileName));
48
    TAndroidHelper.Activity.sendBroadcast(Intent);
49
    Result := True;
50
  except
51
    on E: Exception do begin
52
      if DisplayError then ShowMessage('Error: ' + e.Message);
53

54
      PostLog(llError, Format('MediaScanner Error: [%s]%s', [E.ClassName, E.Message]));
55
    end;
56
  end;
57
end;
58

59
// 这种方式 待测试   FMX.MediaLibrary, FMX.Platform,
60
 var
61
  PhotoService: IFMXPhotoLibrary;
62
  if TPlatformServices.Current.SupportsPlatformService(IFMXPhotoLibrary, PhotoService) then
63
      PhotoService.AddImageToSavedPhotosAlbum(ABitmap,DoWriteImageCompletionEvent);
64

 
 

// 桌面图标显示数字  https://www.cnblogs.com/dannylee/p/7904185.html?tdsourcetag=s_pctim_aiomsg
var
  badgeIntent: JIntent;
begin
//if(Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")){
if TJBuild.JavaClass.MANUFACTURER.equalsIgnoreCase(StringToJString('Xiaomi')) then
begin
        badgeIntent  := TJIntent.Create;
        badgeIntent.setAction(StringToJString('android.intent.action.APPLICATION_MESSAGE_UPDATE'));
        badgeIntent.putExtra(StringToJString('android.intent.extra.update_application_component_name'), getLauncherClassName(context));
        badgeIntent.putExtra(StringToJString('android.intent.extra.update_application_message_text'), count);
end;        
TAndroidHelper.Context.sendBroadcast(badgeIntent);
 
 
 
 
 
 
 
 
 
 
 
1
// 桌面图标显示数字  https://www.cnblogs.com/dannylee/p/7904185.html?tdsourcetag=s_pctim_aiomsg
2
var
3
  badgeIntent: JIntent;
4
begin
5
//if(Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")){
6
if TJBuild.JavaClass.MANUFACTURER.equalsIgnoreCase(StringToJString('Xiaomi')) then
7
begin
8
        badgeIntent  := TJIntent.Create;
9
        badgeIntent.setAction(StringToJString('android.intent.action.APPLICATION_MESSAGE_UPDATE'));
10
        badgeIntent.putExtra(StringToJString('android.intent.extra.update_application_component_name'), getLauncherClassName(context));
11
        badgeIntent.putExtra(StringToJString('android.intent.extra.update_application_message_text'), count);
12
end;        
13
TAndroidHelper.Context.sendBroadcast(badgeIntent);
 
 

// WebBrowser 如何保持原始比例
其实是Delphi在封装WebBrowser的时候,设置了IsAutoFit为True
注释掉 FMX.WebBrowser.Delegate.iOS 单元中的 TNativeWebViewHelper.CreateAndInitWebView  ->  Result.SetScalesPageToFit(True);
或者改页面标记:  <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />  若不行 就去掉 name="viewport" 

判断手机厂商: 如,小米,华为, Vivo, Oppo

 
 
 
 




<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">





posted @ 2020-07-09 15:20  QQ108260  阅读(578)  评论(0)    收藏  举报