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就失去焦点,隐藏虚拟键盘,
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 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) thenTypeface := TJTypeface.JavaClass.createFromFile(StringToJString(FontFile))elseTypeface := 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; // 注意字体图标代码原型是  在Delphi中要将 &#x 要改为 #$ + 后面代码e624
动态转换: char(StrToInt('$'+AItem.Detail5))
用于IOS 的话要修改配置文件 info.plist.TemplateOS.xml,把字体文件名加进去
<key>UIAppFonts</key>
<array>
<string>iconfont.ttf</string>
</array>

截取指定字符串 读取网址的最后文件名如: 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;">
浙公网安备 33010602011771号