HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

Swift那些鲜为人知的特性

iOS

iOS开发中,Swift语言有各种各样缺乏文档记录的特性,这些鲜为人知的特性放在那里,等着我们去发现去使用,现在就让我们看看有哪些特性吧

@transparent
该特性会导致编译器在管道(pipeline)中更早地将函数内联。它用于“像+(Int, Int)这样非常原始的函数”,而“不应该用于独立函数”。
甚至在没有优化设置的调试模式下,@transparent特性函数就会被内联,所以在调用“1+1”这样的函数时候并不会特别慢,另外这个特性与@inline(__always)非常类似。

@availability
这个特性可以用来标识某些函数只在某些平台或版本上可用。第一个参数是平台,可以用星号(*)代表一切可用,还可以是iOS或OS X。因为如果需要针对不同的平台,就要指定多个@availability属性。

如果需要表示该函数在某个给定的平台完全不可用时,可以将第二个参数置为unavailable。此外,还可以用introduced,deprecated和obsoleted来指定一个或是多个版本的组合:obsoleted意味着该项已经删除,deprecated仅仅表示如果使用就会给予警告。最后可以设置message的值,如果该项被使用了就由编译器输出。如果调用另一个被标志为@noreturn的函数,那么编译器会忽略掉当前函数中缺失的返回值(missing return values),因为编译器理解程序的控制流。

@inline
这个特性为编译器提供了内联提示。有效的取值是always和never。除非认为必须要用这两个值,否则就不会使用它(特别是always)。到目前为止与其相关的规则还不是很明确,在有限的测试下,它可以正常地工作,但还要视具体情况而定。
进一步的解释:尽管底层虚拟机(Low Level Virtual Machine, LLVM)有强制内联的概念,但目前还不知道这个@inline特性是否与其直接映射,也不知道是否存在大小方面的限制,但这将会导致编译器忽略这一点而跳过内联。理论上说应该是这样的,但我不保证一定是。
注意(当优化设置关闭时)在调试模式下的构建将忽略@inline。

@asmname
该属性给出了函数、方法或属性实现的符号名称。如果已经知道对应的函数参数及其类型,那么就可以直接调用Swift的内部标准库函数,甚至不用头文件,也可以方便地调用C语言编写的函数:@asmname("function") func f()

@semantics
这又是另一个谜。参数看起来像是array.mutate_unknown或array.init这样的字符串数组。想必这是要告诉编译器(或静态分析器)函数是如何工作的。

@unsafe_no_objc_tagged_pointer
上面这个仍然是个谜,但据猜测,它是在告诉Swift与Objective-C联系的时候不要使用tagged pointer。

继续阅读 »

iOS开发中,Swift语言有各种各样缺乏文档记录的特性,这些鲜为人知的特性放在那里,等着我们去发现去使用,现在就让我们看看有哪些特性吧

@transparent
该特性会导致编译器在管道(pipeline)中更早地将函数内联。它用于“像+(Int, Int)这样非常原始的函数”,而“不应该用于独立函数”。
甚至在没有优化设置的调试模式下,@transparent特性函数就会被内联,所以在调用“1+1”这样的函数时候并不会特别慢,另外这个特性与@inline(__always)非常类似。

@availability
这个特性可以用来标识某些函数只在某些平台或版本上可用。第一个参数是平台,可以用星号(*)代表一切可用,还可以是iOS或OS X。因为如果需要针对不同的平台,就要指定多个@availability属性。

如果需要表示该函数在某个给定的平台完全不可用时,可以将第二个参数置为unavailable。此外,还可以用introduced,deprecated和obsoleted来指定一个或是多个版本的组合:obsoleted意味着该项已经删除,deprecated仅仅表示如果使用就会给予警告。最后可以设置message的值,如果该项被使用了就由编译器输出。如果调用另一个被标志为@noreturn的函数,那么编译器会忽略掉当前函数中缺失的返回值(missing return values),因为编译器理解程序的控制流。

@inline
这个特性为编译器提供了内联提示。有效的取值是always和never。除非认为必须要用这两个值,否则就不会使用它(特别是always)。到目前为止与其相关的规则还不是很明确,在有限的测试下,它可以正常地工作,但还要视具体情况而定。
进一步的解释:尽管底层虚拟机(Low Level Virtual Machine, LLVM)有强制内联的概念,但目前还不知道这个@inline特性是否与其直接映射,也不知道是否存在大小方面的限制,但这将会导致编译器忽略这一点而跳过内联。理论上说应该是这样的,但我不保证一定是。
注意(当优化设置关闭时)在调试模式下的构建将忽略@inline。

@asmname
该属性给出了函数、方法或属性实现的符号名称。如果已经知道对应的函数参数及其类型,那么就可以直接调用Swift的内部标准库函数,甚至不用头文件,也可以方便地调用C语言编写的函数:@asmname("function") func f()

@semantics
这又是另一个谜。参数看起来像是array.mutate_unknown或array.init这样的字符串数组。想必这是要告诉编译器(或静态分析器)函数是如何工作的。

@unsafe_no_objc_tagged_pointer
上面这个仍然是个谜,但据猜测,它是在告诉Swift与Objective-C联系的时候不要使用tagged pointer。

收起阅读 »

Mac版6.7.0真机联调控制台无法使用的解决办法

HBuilder

1.关闭HBuilder

  1. 对HBuilder.app点右键,显示包内容如下图

    3.拷贝jre到Contents目录下(注意是拷贝,不是剪切,这里的jre仍需要保留),我们会在今天晚上(2015-11-9)发布紧急更新
继续阅读 »

1.关闭HBuilder

  1. 对HBuilder.app点右键,显示包内容如下图

    3.拷贝jre到Contents目录下(注意是拷贝,不是剪切,这里的jre仍需要保留),我们会在今天晚上(2015-11-9)发布紧急更新
收起阅读 »

团队承接hbuilder,h5+,mui的外包

外包

如题:有需要外包的请联系我,前端,后台都可以全包,QQ1046373779

如题:有需要外包的请联系我,前端,后台都可以全包,QQ1046373779

iOS平台设置应用访问白名单(LSApplicationQueriesSchemes)

iOS 5+App开发

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-ios-schemewhitelist

从iOS9开始系统策略更新,加入对用户隐私以及禁止扫描系统信息的控制,限制了scheme协议的访问。需要将其它App注册的scheme添加到应用访问白名单(LSApplicationQueriesSchemes)中才能实现以下功能:

  • 通过scheme检查其它App是否安装,不添加到白名单则检测结果为未安装(即使应用已经安装)
  • 通过scheme协议调用其它App,不添加到白名单则会弹出提示框,用户确认后才能启动应用,添加到白名单列表后则无需用户确认直接启动应用。

设置应用跳转白名单列表

打开项目的manifest.json文件,切换到“代码视图”

  • 5+App项目
    在manifest.json文件的"plus"->"distribute"->"apple"下添加urlschemewhitelist节点数据如下:

    "plus": {    
    "distribute": {    
    "apple": {    
        "urlschemewhitelist": [    
                "BaiduSSO",  
                "qqmusic"  
        ],    
        //...    
    },    
    //...    
    },    
    //...    
    },    
    //... 
  • uni-app项目
    把上面的urlschemewhitelist节点数据放到manifest.json的"app-plus"->"distribute"->"ios"节点下

注意:保存后提交App云端打包后才能生效,列表最多可添加50个

App云端打包默认添加的白名单列表

为了方便开发者调用一些常用的第三方应用,云端打包默认已经添加以下白名单

lightsky  
shark.video  
bobo  
snssdk32  
pptv  
bilibili  
kugouURL  
gaeagj  
qqnews  
zhihu  
doubanradio  
openApp.jdMobile  
imeituan  
tmall  
dianping  
vipshop  
yanxuan  
wccbyihaodian  
taobao  
suning  
kaola  
ctrip  
kuaikanmanhua  
gugutouchmanga  
qrxs  
mailmaster  
jcnhers  
wbmain  
yixin  
ydcourse  
ntesopen  
yddict  
shanbay  
tencentweiboSdkv2  
weibosdk2.5  
sinaweibo  
sinaweibohd  
cydia  
weixin  
wechat  
weibosdk  
mqq  
mqqapi  
mqzone  
wtloginmqq2  
mqqopensdkapiV3  
mqqwpa  
mqqopensdkapiV2  
mqqOpensdkSSoL  
hbuilder  
streamapp  
baidumap  
iosamap  
qqmap

iOS平台设置UrlSchemes参考:https://ask.dcloud.net.cn/article/64

继续阅读 »

此文档不再维护,请参考新文档地址:https://uniapp.dcloud.io/tutorial/app-ios-schemewhitelist

从iOS9开始系统策略更新,加入对用户隐私以及禁止扫描系统信息的控制,限制了scheme协议的访问。需要将其它App注册的scheme添加到应用访问白名单(LSApplicationQueriesSchemes)中才能实现以下功能:

  • 通过scheme检查其它App是否安装,不添加到白名单则检测结果为未安装(即使应用已经安装)
  • 通过scheme协议调用其它App,不添加到白名单则会弹出提示框,用户确认后才能启动应用,添加到白名单列表后则无需用户确认直接启动应用。

设置应用跳转白名单列表

打开项目的manifest.json文件,切换到“代码视图”

  • 5+App项目
    在manifest.json文件的"plus"->"distribute"->"apple"下添加urlschemewhitelist节点数据如下:

    "plus": {    
    "distribute": {    
    "apple": {    
        "urlschemewhitelist": [    
                "BaiduSSO",  
                "qqmusic"  
        ],    
        //...    
    },    
    //...    
    },    
    //...    
    },    
    //... 
  • uni-app项目
    把上面的urlschemewhitelist节点数据放到manifest.json的"app-plus"->"distribute"->"ios"节点下

注意:保存后提交App云端打包后才能生效,列表最多可添加50个

App云端打包默认添加的白名单列表

为了方便开发者调用一些常用的第三方应用,云端打包默认已经添加以下白名单

lightsky  
shark.video  
bobo  
snssdk32  
pptv  
bilibili  
kugouURL  
gaeagj  
qqnews  
zhihu  
doubanradio  
openApp.jdMobile  
imeituan  
tmall  
dianping  
vipshop  
yanxuan  
wccbyihaodian  
taobao  
suning  
kaola  
ctrip  
kuaikanmanhua  
gugutouchmanga  
qrxs  
mailmaster  
jcnhers  
wbmain  
yixin  
ydcourse  
ntesopen  
yddict  
shanbay  
tencentweiboSdkv2  
weibosdk2.5  
sinaweibo  
sinaweibohd  
cydia  
weixin  
wechat  
weibosdk  
mqq  
mqqapi  
mqzone  
wtloginmqq2  
mqqopensdkapiV3  
mqqwpa  
mqqopensdkapiV2  
mqqOpensdkSSoL  
hbuilder  
streamapp  
baidumap  
iosamap  
qqmap

iOS平台设置UrlSchemes参考:https://ask.dcloud.net.cn/article/64

收起阅读 »

Android插件,TTS语音合成(文字转化语音)。

插件开发 插件

相关介绍

概念介绍

语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音

合成方式

1.离在线语音合成
2.在线语音合成(本文使用方式)

应用场景

我们团队的应用场景是在推送消息后,需要播放推送消息。

准备事项

1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南
3.下载在线语音合成SDK_Android版。

实现步骤

1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

4.集成java代码

import org.json.JSONArray;  
import com.baidu.speechsynthesizer.SpeechSynthesizer;  
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;  
import com.baidu.speechsynthesizer.publicutility.SpeechError;  
import android.media.AudioManager;  
import android.util.Log;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{  
    private IWebview iwv ;  
    private String text;  
    private SpeechSynthesizer speechSynthesizer;  
    private static final String TAG = "zlz";  
    public void speak(IWebview pWebview, JSONArray array)  
    {  
        iwv = pWebview;  
        String CallBackID = array.optString(0);  
        JSONArray newArray = new JSONArray();  
        newArray.put(array.optString(1));  
        //前台传过来的文本信息  
        text = array.optString(1);  
        //Log.i(TAG, text);  
        initialTts();  
        JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);  
    }  
    //百度TTS初始化  
    private void initialTts() {  
        speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),  
                "holder", this);  
        // 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey  
        speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");  
        speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
        //setVolumeControlStream(AudioManager.STREAM_MUSIC);  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                setParams();  
                int ret = speechSynthesizer.speak(text);  
                if (ret != 0) {  
                    Log.i(TAG,"开始合成器失败" );  
                }  
            }  
        }).start();  

    }  
    private void setParams() {  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);  
    }  
    @Override  
    public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onCancel(SpeechSynthesizer arg0) {}  
    @Override  
    public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}  
    @Override  
    public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}  
    @Override  
    public void onSpeechFinish(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechPause(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onSpeechResume(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechStart(SpeechSynthesizer arg0) {}  
    @Override  
    public void onStartWorking(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSynthesizeFinish(SpeechSynthesizer arg0) {}  
}

5.前台javascript代码

document.addEventListener( "plusready",  function()  
{  
    var _BARCODE = 'kxdPlugins',  
        B = window.plus.bridge;  
    var kxdPlugins =   
    {  
        // 声明异步返回方法  
        speak : function (Argus, successCallback, errorCallback )   
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)   
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)   
            {  
                errorCallback(code);  
            };  
            callbackID = B.callbackId(success, fail);  
            // 通知Native层plugintest扩展插件运行”speak”方法  
            return B.exec(_BARCODE, "speak", [callbackID, Argus]);  
        }  
    };  
    window.plus.kxdPlugins = kxdPlugins;  
}, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");

注意事项

1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

继续阅读 »

相关介绍

概念介绍

语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。
对本文中将提到的概念约定如下:
语音合成:将文本合成为语音,即声音文件。
合成引擎:将文本合成为语音的核心模块。
TTS:Text To Speech,即“从文本到语音”。
BDTTSClient:语音合成 SDK 简称,详见下条。
语音合成 SDK:即本开发包,文中简称为 BDTTSClient。BDTTSClient 是一个封装了网络收发、音频播放功能的语音合成解决方案。借助 BDTTSClient 可以快速地在应用程序中集成语音合成功能。
百度语音

合成方式

1.离在线语音合成
2.在线语音合成(本文使用方式)

应用场景

我们团队的应用场景是在推送消息后,需要播放推送消息。

准备事项

1.下载Android 平台 HTML5+ SDK 。
2.注册百度开发者,创建应用,开通服务,请参考集成指南
3.下载在线语音合成SDK_Android版。

实现步骤

1.先导入5+SDK就不多说了,参考Android平台第三方插件开发指导
2.添加BDTTSClient到工程(将开发包中的 libs 目录整体拷贝到工程目录,libs 目录包括了各平台的 SO 库,开发者视应用需要可以进行删减。galaxy_lite.jar 是百度 Android 公共基础库。)
3.权限声明(需要在 AndroidManifest.xml 文件,增加以上三个权限)

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

4.集成java代码

import org.json.JSONArray;  
import com.baidu.speechsynthesizer.SpeechSynthesizer;  
import com.baidu.speechsynthesizer.SpeechSynthesizerListener;  
import com.baidu.speechsynthesizer.publicutility.SpeechError;  
import android.media.AudioManager;  
import android.util.Log;  
import io.dcloud.common.DHInterface.IWebview;  
import io.dcloud.common.DHInterface.StandardFeature;  
import io.dcloud.common.util.JSUtil;  
public class baidutts extends StandardFeature implements SpeechSynthesizerListener{  
    private IWebview iwv ;  
    private String text;  
    private SpeechSynthesizer speechSynthesizer;  
    private static final String TAG = "zlz";  
    public void speak(IWebview pWebview, JSONArray array)  
    {  
        iwv = pWebview;  
        String CallBackID = array.optString(0);  
        JSONArray newArray = new JSONArray();  
        newArray.put(array.optString(1));  
        //前台传过来的文本信息  
        text = array.optString(1);  
        //Log.i(TAG, text);  
        initialTts();  
        JSUtil.execCallback(pWebview, CallBackID, newArray, JSUtil.OK, false);  
    }  
    //百度TTS初始化  
    private void initialTts() {  
        speechSynthesizer = new SpeechSynthesizer(iwv.getContext(),  
                "holder", this);  
        // 此处需要将setApiKey方法的两个参数替换为你在百度开发者中心注册应用所得到的apiKey和secretKey  
        speechSynthesizer.setApiKey("your-apiKey", "your-secretKey");  
        speechSynthesizer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
        //setVolumeControlStream(AudioManager.STREAM_MUSIC);  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                setParams();  
                int ret = speechSynthesizer.speak(text);  
                if (ret != 0) {  
                    Log.i(TAG,"开始合成器失败" );  
                }  
            }  
        }).start();  

    }  
    private void setParams() {  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_ENCODE, SpeechSynthesizer.AUDIO_ENCODE_AMR);  
        speechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUDIO_RATE, SpeechSynthesizer.AUDIO_BITRATE_AMR_15K85);  
    }  
    @Override  
    public void onBufferProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onCancel(SpeechSynthesizer arg0) {}  
    @Override  
    public void onError(SpeechSynthesizer arg0, SpeechError arg1) {}  
    @Override  
    public void onNewDataArrive(SpeechSynthesizer arg0, byte[] arg1, boolean arg2) {}  
    @Override  
    public void onSpeechFinish(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechPause(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechProgressChanged(SpeechSynthesizer arg0, int arg1) {}  
    @Override  
    public void onSpeechResume(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSpeechStart(SpeechSynthesizer arg0) {}  
    @Override  
    public void onStartWorking(SpeechSynthesizer arg0) {}  
    @Override  
    public void onSynthesizeFinish(SpeechSynthesizer arg0) {}  
}

5.前台javascript代码

document.addEventListener( "plusready",  function()  
{  
    var _BARCODE = 'kxdPlugins',  
        B = window.plus.bridge;  
    var kxdPlugins =   
    {  
        // 声明异步返回方法  
        speak : function (Argus, successCallback, errorCallback )   
        {  
            var success = typeof successCallback !== 'function' ? null : function(args)   
            {  
                successCallback(args);  
            },  
            fail = typeof errorCallback !== 'function' ? null : function(code)   
            {  
                errorCallback(code);  
            };  
            callbackID = B.callbackId(success, fail);  
            // 通知Native层plugintest扩展插件运行”speak”方法  
            return B.exec(_BARCODE, "speak", [callbackID, Argus]);  
        }  
    };  
    window.plus.kxdPlugins = kxdPlugins;  
}, true );

使用方式

和普通的API调用方式一样:plus.kxdPlugins.speak("这是语音播放的文字内容");

注意事项

1.在/assets/data/properties.xml中添加插件权限。
2.在/assets/apps/appid/www/manifest.json 中添加对应插件权限。
3.在/AndroidManifest.xml 文件中添加对应的百度语音合成权限。

收起阅读 »

获取图片当前旋转状态

Orientation

今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。

function getImgOrientation (filePath) {  
            var ExifInterface = plus.android.importClass("android.media.ExifInterface");  
            var exifInterface = new ExifInterface(filePath.replace("file:///", ""));  
            var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);  
            var degree = 0;  
            switch (orientation) {  
                case ExifInterface.ORIENTATION_ROTATE_90:  
                    degree = 90;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_180:  
                    degree = 180;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_270:  
                    degree = 270;  
                    break;  
            }  
            return degree;  
        }
继续阅读 »

今日开发过程中遇见从相册选择或者自拍照片,显示时为横向。故封装如下代码,希望对大家有所帮助。

function getImgOrientation (filePath) {  
            var ExifInterface = plus.android.importClass("android.media.ExifInterface");  
            var exifInterface = new ExifInterface(filePath.replace("file:///", ""));  
            var orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);  
            var degree = 0;  
            switch (orientation) {  
                case ExifInterface.ORIENTATION_ROTATE_90:  
                    degree = 90;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_180:  
                    degree = 180;  
                    break;  
                case ExifInterface.ORIENTATION_ROTATE_270:  
                    degree = 270;  
                    break;  
            }  
            return degree;  
        }
收起阅读 »

整包(apk/ipa)升级

升级

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

  • 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
  • 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
  • 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。

本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:

  1. 查询是否有新版本更新
  2. 下载新版本
  3. 安装新版本

而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。

查询是否有新版本更新

这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:

  1. 客户端判断是否有升级
    客户端从服务器获取最新的版本号,本地js判断是否需要升级。
  2. 服务器判断是否有升级
    客户端提交版本到服务器,有服务器判断返回是否需要升级。
    前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。

可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest

下载新版本

如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:

var url=""; // 下载文件地址  
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {  
    if ( status == 200 ) { // 下载成功  
        var path = d.filename;  
        console.log(d.filename);  
    } else {//下载失败  
        alert( "Download failed: " + status );   
    }    
});  
dtask.start(); 

安装新版本

下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:

    plus.runtime.install(path);  // 安装下载的apk文件

注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:

var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址  
plus.runtime.openURL(url);

此处url是以"itms-apps://"开头,后面跟appstore上应用地址。

示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。

继续阅读 »

官方已发布APP升级中心,支持原生APP整包升级和wgt资源包升级。详见

5+应用可使用以下方式进行升级

  • 整包升级:适用于大版本更新,runtime发生变化时(模块、配置、版本等变化)必须使用此更新方法
  • 应用资源升级:适用于小版本更新 。runtime不变,前端页面整体压缩包更新
  • 应用资源差量升级:适用于小版本更新 。runtime不变,前端页面仅需要更新的部分更新。

本文重点描述5+应用整包升级,逻辑其实是非常简单,主要有三个步骤:

  1. 查询是否有新版本更新
  2. 下载新版本
  3. 安装新版本

而在实际应用的升级过程中则需要处理很多细节问题,下面就按照这个流程来简单说明在5+App中实现应用升级的逻辑。

查询是否有新版本更新

这个操作其实非常简单,就是客户端与升级服务器的一次交互操作,比较升级服务器上发布的最新客户端版本是否高于当前客户端版本号(5+ API中可以通过plus.runtime.version获取当前apk/ipa的版本号,注意打包方生效)?是的话则需要升级,否则无需升级。
从逻辑上来考虑有两种判断模式:

  1. 客户端判断是否有升级
    客户端从服务器获取最新的版本号,本地js判断是否需要升级。
  2. 服务器判断是否有升级
    客户端提交版本到服务器,有服务器判断返回是否需要升级。
    前者的优点是否服务器压力小,静态返回最新客户端版本即可,后者的优点则升级控制会更灵活,可以根据其它条件动态控制部分用户先升级(灰度发布)等。有条件的情况推荐采用第二种方式进行判断。

可以使用Javascript中的标准XHR请求,如果存在跨域问题则使用5+ API的XMLHttpRequest

下载新版本

如果判断到需要更新版本,则需要从服务器下载新版本,通常升级服务器应该返回下载新版本的地址(或者从固定的地址获取)。
有两种下载方法,一种调用Downloader API下载,示例如下:

var url=""; // 下载文件地址  
var dtask = plus.downloader.createDownload( url, {}, function ( d, status ) {  
    if ( status == 200 ) { // 下载成功  
        var path = d.filename;  
        console.log(d.filename);  
    } else {//下载失败  
        alert( "Download failed: " + status );   
    }    
});  
dtask.start(); 

安装新版本

下载原生安装包apk后,可调用[plus.runtime.install]()方法安装,示例如下:

    plus.runtime.install(path);  // 安装下载的apk文件

注意
iOS平台的ipa无法安装,此时需要跳转到appstore,提示用户自动点击升级更新,跳转到appstore的方法为打开应用的appstore地址,示例如下:

var url='itms-apps://itunes.apple.com/cn/app/hello-h5+/id682211190?l=zh&mt=8';// HelloH5应用在appstore的地址  
plus.runtime.openURL(url);

此处url是以"itms-apps://"开头,后面跟appstore上应用地址。

示例
在Hello H5+和Hello mui示例里,有2种不同的检查更新处理方式。
Hello H5+里的update.js比较复杂,在js里执行下载apk并安装的逻辑。
Hello mui里的update.js比较简单,弹出新包下载地址到浏览器,由浏览器执行下载逻辑。
如何选择看自己的要求。

收起阅读 »

低价外包H5移动端页面

外包

纯切图,css3动画

纯切图,css3动画

如何在本地进行微信公众号的开发调试

在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。

现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机

原理

调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。

使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试

继续阅读 »

在进行微信公众号开发时,以前必须要有外网域名才能收到微信服务器的推送,这给开发和调试带来很大的不便。

现在方便了,QQ浏览器提供了微信公众号调试工具,开发者可以在自己的开发机上进行开发和调试,不再需要外网IP和域名。不仅可以直观看到已接收和已发送的消息内容和事件,方便开发和问题定位,还可以重复发送已接收的微信报文,避免调试时频繁操作手机

原理

调试工具内置了一个server,监听你本地运行的程序,并为你生成一个外网的url,你只需要在公众号的开发信息中配置上这个url,公众号收到消息后,就会通过这个url转发到调试工具上,调试工具再转给你的本地运行程序。

使用方法
(1)在本机启动运行自己程序的server
(2)打开qq浏览器,在应用中心中安装“微信调试工具”,安装完成后,选择“服务器端调试”
(3)填写本地server的ip和端口号,调试工具会返回一个外部URL
(4)到公众号后台开发者配置信息中填写这个URL
(5)用微信向公众号发送消息测试

收起阅读 »

IOS平台:如何在离线打包时配置 3D Touch图标快捷菜单

App离线打包 iOS

Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:

离线工程配置的方法

打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。

各字段值规范如下表
各值的关系请参考上图
节点名 节点类型 可选性 节点说明 备注
UIApplicationShortcutItems Array 必选 图标快捷菜单节点声明
UIApplicationShortcutItemType String 必选 菜单类型
UIApplicationShortcutItemTitle String 必选 菜单项的标题
UIApplicationShortcutItemSubtitle String 可选 菜单项的副标题
UIApplicationShortcutItemIconFile String 可选 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType
UIApplicationShortcutItemIconType String 可选 指定菜单项显示系统自带图标 取值可参考苹果官方文档
UIApplicationShortcutItemUserInfo String 可选 用户自定义的菜单项附加信息 附带信息可在应用内通过plus.runtime.arguments获取

App中处理快捷菜单项

请参考文档iOS平台支持3D Touch快捷菜单项

继续阅读 »

Apple最新发布的iPhone 6s (plus) 设备支持3D touch功能,用户可通过重按屏幕上的图标,打开设置的菜单。H5+ SDK已经支持添加应用的快捷菜单,显示效果如下:

离线工程配置的方法

打开离线打包工程的info.plist文件,在文件中添加UIApplicationShortcutItems节点,并在节点下添加item。

各字段值规范如下表
各值的关系请参考上图
节点名 节点类型 可选性 节点说明 备注
UIApplicationShortcutItems Array 必选 图标快捷菜单节点声明
UIApplicationShortcutItemType String 必选 菜单类型
UIApplicationShortcutItemTitle String 必选 菜单项的标题
UIApplicationShortcutItemSubtitle String 可选 菜单项的副标题
UIApplicationShortcutItemIconFile String 可选 指定菜单项显示安装包内自带的图标,相对安装包的根目录位置 与UIApplicationShortcutItemIconType节点同时配置时优先级高于UIApplicationShortcutItemIconType
UIApplicationShortcutItemIconType String 可选 指定菜单项显示系统自带图标 取值可参考苹果官方文档
UIApplicationShortcutItemUserInfo String 可选 用户自定义的菜单项附加信息 附带信息可在应用内通过plus.runtime.arguments获取

App中处理快捷菜单项

请参考文档iOS平台支持3D Touch快捷菜单项

收起阅读 »

iOS平台支持3D Touch快捷菜单项

3DTouch 5+App开发 iOS

iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:

配置快捷菜单项

打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:

    "shortcuts": [  
        {  
            "type": "share",  
            "title": "分 享",  
            "subtitle": "分享到微信、微博、QQ",  
            "icontype": "UIApplicationShortcutIconTypeShare"  
        },  
        {  
            "type": "about",  
            "title": "关 于",  
            "subtitle": "www.dcloud.io",  
            "iconfile": "sa.png",  
            "userinfo": {  
                "key3":"value3"  
            }  
        }  
    ],

uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:

  • type: (必选)菜单项类型,字符串类型,用于标识菜单项
  • title: (必选)菜单项上显示的标题,字符串类型
  • subtitle: (可选)菜单项上显示的子标题,字符串类型
  • icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
  • iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
  • userinfo: (可选)菜单项上的自定义数据,JSON格式

App中处理快捷菜单项

判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:

if(plus.runtime.launcher=='shortcut'){  
     // ...  
}

判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:

var cmd = JSON.parse(plus.runtime.arguments);  
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)  
var type=cmd&&cmd.type;  
switch(type){  
    case 'share':  
        // 用户点击了‘share'菜单项  
    break;  
    case 'about':  
        // 用户点击了’about'菜单项  
    break;  
    default:  
    break;  
}

plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:

  • type: 菜单项类型,字符串类型
  • title: 菜单项上显示的标题,字符串类型
  • subtitle: 菜单项上显示的子标题,字符串类型
  • userinfo: 菜单项上的自定义数据

注:真机运行不生效,需提交App云端打包后才生效

实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法

uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103

继续阅读 »

iPhone6s(plus)设备已经支持3D Touch屏幕,HBuilder已支持添加应用的快捷菜单,效果如下:

配置快捷菜单项

打开应用的manifest.json文件,切换到代码视图,在plus -> distribute -> apple 下添加shortcuts节点,并配置各菜单项,数组中每项对应一个快捷菜单项:

    "shortcuts": [  
        {  
            "type": "share",  
            "title": "分 享",  
            "subtitle": "分享到微信、微博、QQ",  
            "icontype": "UIApplicationShortcutIconTypeShare"  
        },  
        {  
            "type": "about",  
            "title": "关 于",  
            "subtitle": "www.dcloud.io",  
            "iconfile": "sa.png",  
            "userinfo": {  
                "key3":"value3"  
            }  
        }  
    ],

uni-app项目将以上数据添加到"app-plus"->"distribute"->"ios"节点下
其中各字段值规范如下:

  • type: (必选)菜单项类型,字符串类型,用于标识菜单项
  • title: (必选)菜单项上显示的标题,字符串类型
  • subtitle: (可选)菜单项上显示的子标题,字符串类型
  • icontype: (可选)菜单项上显示的图标类型,字符串类型,取值参考iOS官方文档UIApplicationShortcutIconType
  • iconfile: (可选)菜单项上显示的图标文件,字符串类型,相对5+应用根目录路径,图标要求35x35分辨率,单色,参考Apple官方PS模板图
  • userinfo: (可选)菜单项上的自定义数据,JSON格式

App中处理快捷菜单项

判断应用是否通过快捷菜单项启动
plus.runtime.launcher的值为"shortcut"表示应用从快捷菜单项启动,示例如下:

if(plus.runtime.launcher=='shortcut'){  
     // ...  
}

判断快捷菜单项启动的参数
plus.runtime.arguments中保存所有快捷菜单项配置的值(JSON格式字符串),示例如下:

var cmd = JSON.parse(plus.runtime.arguments);  
console.log("Shortcut-plus.runtime.arguments: "+plus.runtime.arguments)  
var type=cmd&&cmd.type;  
switch(type){  
    case 'share':  
        // 用户点击了‘share'菜单项  
    break;  
    case 'about':  
        // 用户点击了’about'菜单项  
    break;  
    default:  
    break;  
}

plus.runtime.arguments的JSON格式字符串示例如下:
“{"type":"about","title":"关 于","subtitle": "www.dcloud.io","userinfo":{"key3":"value3"}}”
其中包括以下键值:

  • type: 菜单项类型,字符串类型
  • title: 菜单项上显示的标题,字符串类型
  • subtitle: 菜单项上显示的子标题,字符串类型
  • userinfo: 菜单项上的自定义数据

注:真机运行不生效,需提交App云端打包后才生效

实际用法参考HelloH5应用的“js/shortcut.js”
iOS平台5+SDK原生环境配置方法

uni-app网友经验分享:https://ask.dcloud.net.cn/article/36103

收起阅读 »

如何动态兼容沉浸式状态栏模式

UserAgent navigator 沉浸式状态栏 5+App开发

HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:

  1. Android平台设置沉浸式状态栏显示效果
  2. iOS平台设置沉浸式状态栏显示效果

由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:

使用5+API

  • 判断当前环境是否支持沉浸式状态栏
    plus.navigator.isImmersedStatusbar()
    如果当前支持沉浸式状态栏则返回true,否则返回false。
  • 获取当前系统状态栏高度
    plus.navigator.getStatusbarHeight()
    获取系统状态栏高度,Number类型。
    其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。

实际用法参考HelloH5应用的“plus/doc.html”:

    // 创建加载内容窗口  
    var topoffset='45px';  
    if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式  
        // 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置  
        topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';  
    }  
    // 使用偏移位置创建子窗口  
    wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});

通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。

可以使用正则表达式进行获取:

var immersed = 0;  
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);  
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式  
    immersed=parseFloat(ms[2]);// 获取状态栏的高度  
}

immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:

var t=document.getElementById('header');  
t&&t.style.paddingTop=immersed+'px';

具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”

继续阅读 »

HBuilder6.6.1版本已经完全支持沉浸式状态栏,可以下载最新版本HelloH5应用体验。
各平台配置参考:

  1. Android平台设置沉浸式状态栏显示效果
  2. iOS平台设置沉浸式状态栏显示效果

由于各系统版本的限制,沉浸式状态栏对系统有要求(Android4.4及以上、iOS7.0及以上),如果要兼容各系统版本,需要动态判断当前环境是否支持沉浸式状态栏以及系统状态栏的高度:

使用5+API

  • 判断当前环境是否支持沉浸式状态栏
    plus.navigator.isImmersedStatusbar()
    如果当前支持沉浸式状态栏则返回true,否则返回false。
  • 获取当前系统状态栏高度
    plus.navigator.getStatusbarHeight()
    获取系统状态栏高度,Number类型。
    其单位是逻辑像素值,即css中可直接使用的像素值,可能存在小数点。

实际用法参考HelloH5应用的“plus/doc.html”:

    // 创建加载内容窗口  
    var topoffset='45px';  
    if(plus.navigator.isImmersedStatusbar()){// 兼容immersed状态栏模式  
        // 获取状态栏高度并根据业务需求处理,这里重新计算了子窗口的偏移位置  
        topoffset=(Math.round(plus.navigator.getStatusbarHeight())+45)+'px';  
    }  
    // 使用偏移位置创建子窗口  
    wc=plus.webview.create(null,'doccontent',{top:topoffset,bottom:'0px',bounce:'vertical',bounceBackground:'#FFFFFF'});

通过userAgent判断
5+API需要在plusready事件后才能调用,通常此事件在DOM加载渲染后才会触发,无法再渲染前根据不同的状态来设置css。
为了解决此问题,在支持5+API运行环境的userAgent中特定字段Html5Plus/1.0后添加Immersed标识,如下:
"Html5Plus/1.0 (Immersed/30)"
其中Immersed/后的30表示状态栏的高度,单位为逻辑像素值。

可以使用正则表达式进行获取:

var immersed = 0;  
var ms=(/Html5Plus\/.+\s\(.*(Immersed\/(\d+\.?\d*).*)\)/gi).exec(navigator.userAgent);  
if(ms&&ms.length>=3){ // 当前环境为沉浸式状态栏模式  
    immersed=parseFloat(ms[2]);// 获取状态栏的高度  
}

immersed值如果大于0则表示当前环境支持沉浸式状态栏。
获取状态栏高度后,可以使用js动态修改DOM元素的css属性来设置样式,如设置界面头区域的顶部内边距为状态栏的高度(避免系统状态栏与界面头重叠),示例如下:

var t=document.getElementById('header');  
t&&t.style.paddingTop=immersed+'px';

具体项目中可根据界面设计,灵活使用immersed值来动态适配各种效果。
完整用法可参考HelloH5应用中的“js/immersed.js”

收起阅读 »