
PDA终端扫描实现
以下方法都是非原生方式实现
Javascript获取扫码结果实现
document.onkeydown=function(e){
//webview不需要兼容ie
console.log(e.keyCode)
}
该方法需要判断按键和间隔输入情况,用户可能在扫码的时候有其他按键等误操作,所以不建议用来进行获取扫码结果。
Native.js监听广播消息实现
参考论坛 回答
function plusReady() {
var main = plus.android.runtimeMainActivity();//获取activity
var context = plus.android.importClass('android.content.Context'); //上下文
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{
onReceive : doReceive });
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction("com.zkc.scancode");//监听扫描
main.registerReceiver(receiver,filter);//注册监听
function doReceive(context, intent) {
plus.android.importClass(intent);//通过intent实例引入intent类,方便以后的‘.’操作
var Number = intent.getStringExtra("code");
console.log(Number);
main.unregisterReceiver(receiver);//取消监听
}
}
销邦X8扫描枪
我这里使用的是销邦科技X8扫描设备,由于设备不一样导致文章中的代码不能直接使用,需要配置监听的数据才能连接。搜索销邦官网获取对应开发包下载
注意设置内勾选开启扫描,选择输入模式API:
打开开发说明查看参数:
代码对应修改如下:
···
filter.addAction("com.android.server.scannerservice.broadcast");//监听扫描
···
intent.getStringExtra("scannerdata");//返回结果
···
原代码实现的是扫码完成就取消监听这样不符合实际业务(多次扫描),但是不取消监听用同时有打开新的页面,这时会发现之前的页面还在监听扫描,当然在打开新页面的时候进行取消监听,但是当用户从新页面返回之前的页面时又需要重启监听,各种条件判断写出了比较麻烦。
这里采用的是接受扫描结果时判断是否是当前显示页面。首先在打开页面时把页面id存储到本地,并且重写back:
function pageInit(){
var _self = plus.webview.currentWebview()
localStorage.setItem('WEBVIEW_ID', _self.id)
mui.back = function() {
localStorage.setItem('WEBVIEW_ID', _self.opener().id)
_self.close()
}
}
然后判断页面id:
function scan(callback){
var main = plus.android.runtimeMainActivity(); //获取activity
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive: function(context, intent){//实现onReceiver回调函数
if(plus.webview.currentWebview().id != localStorage.getItem(util.config.WEBVIEW_ID)) return
callback(intent.getStringExtra('scannerdata'))
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');//引入过滤器
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction('com.android.server.scannerservice.broadcast'); //监听扫码广播
main.registerReceiver(receiver, filter); //注册监听
}
页面使用:
mui.plusReady(function () {
pageInit()
setTimeout(function(){
scan(function(code){
console.log('扫描结果:'+code)
})
},300)
})
注意:在HBuilder控制台没有调试信息,需要在webview调试窗口中才能看到打印信息。另建议在plusReady中延迟执行scan()。
霍尼韦尔扫描枪
首先需要在设备上去设置监听的对象名(我这里设置成和销邦设备一样的com.android.server.scannerservice.broadcast),接受参数的时候再判断一下设备plus.device.model进行切换接受返回结果的对象名:
scan: function(callback){
var main = plus.android.runtimeMainActivity(); //获取activity
var context = plus.android.importClass('android.content.Context'); //上下文
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive: function(context, intent){//实现onReceiver回调函数
if(plus.webview.currentWebview().id != localStorage.getItem(util.config.WEBVIEW_ID)) return
var _key = plus.device.model == 'EDA50K' ? 'data' : 'scannerdata';
callback(intent.getStringExtra(_key))
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');//引入过滤器
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction('com.android.server.scannerservice.broadcast'); //监听扫码广播
main.registerReceiver(receiver, filter); //注册监听
}
以下方法都是非原生方式实现
Javascript获取扫码结果实现
document.onkeydown=function(e){
//webview不需要兼容ie
console.log(e.keyCode)
}
该方法需要判断按键和间隔输入情况,用户可能在扫码的时候有其他按键等误操作,所以不建议用来进行获取扫码结果。
Native.js监听广播消息实现
参考论坛 回答
function plusReady() {
var main = plus.android.runtimeMainActivity();//获取activity
var context = plus.android.importClass('android.content.Context'); //上下文
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver',{
onReceive : doReceive });
var IntentFilter = plus.android.importClass('android.content.IntentFilter');
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction("com.zkc.scancode");//监听扫描
main.registerReceiver(receiver,filter);//注册监听
function doReceive(context, intent) {
plus.android.importClass(intent);//通过intent实例引入intent类,方便以后的‘.’操作
var Number = intent.getStringExtra("code");
console.log(Number);
main.unregisterReceiver(receiver);//取消监听
}
}
销邦X8扫描枪
我这里使用的是销邦科技X8扫描设备,由于设备不一样导致文章中的代码不能直接使用,需要配置监听的数据才能连接。搜索销邦官网获取对应开发包下载
注意设置内勾选开启扫描,选择输入模式API:
打开开发说明查看参数:
代码对应修改如下:
···
filter.addAction("com.android.server.scannerservice.broadcast");//监听扫描
···
intent.getStringExtra("scannerdata");//返回结果
···
原代码实现的是扫码完成就取消监听这样不符合实际业务(多次扫描),但是不取消监听用同时有打开新的页面,这时会发现之前的页面还在监听扫描,当然在打开新页面的时候进行取消监听,但是当用户从新页面返回之前的页面时又需要重启监听,各种条件判断写出了比较麻烦。
这里采用的是接受扫描结果时判断是否是当前显示页面。首先在打开页面时把页面id存储到本地,并且重写back:
function pageInit(){
var _self = plus.webview.currentWebview()
localStorage.setItem('WEBVIEW_ID', _self.id)
mui.back = function() {
localStorage.setItem('WEBVIEW_ID', _self.opener().id)
_self.close()
}
}
然后判断页面id:
function scan(callback){
var main = plus.android.runtimeMainActivity(); //获取activity
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive: function(context, intent){//实现onReceiver回调函数
if(plus.webview.currentWebview().id != localStorage.getItem(util.config.WEBVIEW_ID)) return
callback(intent.getStringExtra('scannerdata'))
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');//引入过滤器
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction('com.android.server.scannerservice.broadcast'); //监听扫码广播
main.registerReceiver(receiver, filter); //注册监听
}
页面使用:
mui.plusReady(function () {
pageInit()
setTimeout(function(){
scan(function(code){
console.log('扫描结果:'+code)
})
},300)
})
注意:在HBuilder控制台没有调试信息,需要在webview调试窗口中才能看到打印信息。另建议在plusReady中延迟执行scan()。
霍尼韦尔扫描枪
首先需要在设备上去设置监听的对象名(我这里设置成和销邦设备一样的com.android.server.scannerservice.broadcast),接受参数的时候再判断一下设备plus.device.model进行切换接受返回结果的对象名:
scan: function(callback){
var main = plus.android.runtimeMainActivity(); //获取activity
var context = plus.android.importClass('android.content.Context'); //上下文
var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
onReceive: function(context, intent){//实现onReceiver回调函数
if(plus.webview.currentWebview().id != localStorage.getItem(util.config.WEBVIEW_ID)) return
var _key = plus.device.model == 'EDA50K' ? 'data' : 'scannerdata';
callback(intent.getStringExtra(_key))
}
});
var IntentFilter = plus.android.importClass('android.content.IntentFilter');//引入过滤器
var Intent = plus.android.importClass('android.content.Intent');
var filter = new IntentFilter();
filter.addAction('com.android.server.scannerservice.broadcast'); //监听扫码广播
main.registerReceiver(receiver, filter); //注册监听
}
收起阅读 »

快递连续扫码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link href="../css/mui.min.css" rel="stylesheet" />
<link href="../css/iconfont.css" rel="stylesheet" />
<link href="../css/common.css" rel="stylesheet" />
<link href="../css/style.css" rel="stylesheet" />
<script src="../js/mui.min.js"></script>
<script src="../js/public.js"></script>
<style>
#bcid {
height: 280px;
width:300px;
}
</style>
</head>
<body>
<div class="scan-img-div" style="display: none;">
<div id="bcid" >
</div>
</div>
<div class="padding-lr20">
<div class="scan-result-div">
<div class="wxrow">
<div class='wxcenter padding-l5 width60 size12 bold'>下一站:</div>
<div class='flex1 relative'>
<select class="mui-btn send-select">
<option>请选择下一站</option>
<option value="item-1">item-1</option>
<option value="item-2">item-2</option>
<option value="item-3">item-3</option>
<option value="item-4">item-4</option>
<option value="item-5">item-5</option>
</select>
<span class="iconfont icon-xiajiantou po-down"></span>
</div>
</div>
<div class="wxrow padding-lr5 padding-b10 margin-b10 margin-t30 bor-b-g2 size12">
<div class="flex1 bold">扫描结果</div>
<div class="">
<span class="b-999">票数:</span>
<span class="g-009999">8xx</span>
</div>
</div>
<div class="wxrow margin-t10 padding-lr10">
<div class="b-333 margin-r15">589078476897438xxx</div>
<div class="width40 wxaround">
<span class="iconfont icon-cha r-ff0000"></span>
</div>
</div>
<div class="wxrow margin-t10 padding-lr10">
<div class="b-333 margin-r15">589078476897438xxx</div>
<div class="width40 wxaround margin-l40">
<span class="iconfont icon-cha r-ff0000"></span>
</div>
</div>
</div>
</div>
<div class="fix-b-scan wxrow">
<div class="flex1 scan-btn bg-o-ff6600" id="scan-go">确认出仓</div>
<div class="flex2 scan-btn bg-b-383838" onclick="openScan()">扫描</div>
</div>
<script type="text/javascript">
console.log(1)
mui.init()
let scan = null;
function openScan() {
ddsd.qs('.scan-img-div').style.display = 'block'
scan = new plus.barcode.Barcode('bcid', [
plus.barcode.EAN13,
plus.barcode.EAN8,
plus.barcode.CODE39,
plus.barcode.CODE93,
plus.barcode.CODE128
], {
top:'1000px',
left:'0px',
width: '100%',
height: '100px',
position: 'static',
frameColor: '#00FF00',
scanbarColor: '#00FF00'
});
scan.onmarked = onmarked;
scan.start();
}
mui.plusReady(function() {
function onmarked(type, result) {
var text = '未知: ';
switch(type) {
case plus.barcode.QR:
text = 'QR: ';
break;
case plus.barcode.EAN13:
text = 'EAN13: ';
break;
case plus.barcode.EAN8:
text = 'EAN8: ';
break;
case plus.barcode.CODE39:
text = 'CODE39: ';
break;
case plus.barcode.CODE93:
text = 'CODE93: ';
break;
case plus.barcode.CODE128:
text = 'CODE128: ';
break;
}
console.log(text + result);
if(result) {
console.log(result)
// scan.close();
// window.location.reload();
scan.cancel();
setTimeout(() => {
scan.start();
},1000)
}
}
mui("body").on('tap', '#scan-go', function() {
mui.openWindow({
url: 'scan_success.html',
id: 'scan_success.html',
styles: { // 窗口参数 参考5+规范中的WebviewStyle,也就是说WebviewStyle下的参数都可以在此设置
titleNView: { // 窗口的标题栏控件
autoBackButton: true,
titleText: '出仓成功', // 标题栏文字,当不设置此属性时,默认加载当前页面的标题,并自动更新页面的标题
titleColor: "#000000", // 字体颜色,颜色值格式为"#RRGGBB",默认值为"#000000"
titleSize: "17px", // 字体大小,默认17px
backgroundColor: "#FFCC00", // 控件背景颜色,颜色值格式为"#RRGGBB",默认值为"#F7F7F7"
}
}
});
})
})
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link href="../css/mui.min.css" rel="stylesheet" />
<link href="../css/iconfont.css" rel="stylesheet" />
<link href="../css/common.css" rel="stylesheet" />
<link href="../css/style.css" rel="stylesheet" />
<script src="../js/mui.min.js"></script>
<script src="../js/public.js"></script>
<style>
#bcid {
height: 280px;
width:300px;
}
</style>
</head>
<body>
<div class="scan-img-div" style="display: none;">
<div id="bcid" >
</div>
</div>
<div class="padding-lr20">
<div class="scan-result-div">
<div class="wxrow">
<div class='wxcenter padding-l5 width60 size12 bold'>下一站:</div>
<div class='flex1 relative'>
<select class="mui-btn send-select">
<option>请选择下一站</option>
<option value="item-1">item-1</option>
<option value="item-2">item-2</option>
<option value="item-3">item-3</option>
<option value="item-4">item-4</option>
<option value="item-5">item-5</option>
</select>
<span class="iconfont icon-xiajiantou po-down"></span>
</div>
</div>
<div class="wxrow padding-lr5 padding-b10 margin-b10 margin-t30 bor-b-g2 size12">
<div class="flex1 bold">扫描结果</div>
<div class="">
<span class="b-999">票数:</span>
<span class="g-009999">8xx</span>
</div>
</div>
<div class="wxrow margin-t10 padding-lr10">
<div class="b-333 margin-r15">589078476897438xxx</div>
<div class="width40 wxaround">
<span class="iconfont icon-cha r-ff0000"></span>
</div>
</div>
<div class="wxrow margin-t10 padding-lr10">
<div class="b-333 margin-r15">589078476897438xxx</div>
<div class="width40 wxaround margin-l40">
<span class="iconfont icon-cha r-ff0000"></span>
</div>
</div>
</div>
</div>
<div class="fix-b-scan wxrow">
<div class="flex1 scan-btn bg-o-ff6600" id="scan-go">确认出仓</div>
<div class="flex2 scan-btn bg-b-383838" onclick="openScan()">扫描</div>
</div>
<script type="text/javascript">
console.log(1)
mui.init()
let scan = null;
function openScan() {
ddsd.qs('.scan-img-div').style.display = 'block'
scan = new plus.barcode.Barcode('bcid', [
plus.barcode.EAN13,
plus.barcode.EAN8,
plus.barcode.CODE39,
plus.barcode.CODE93,
plus.barcode.CODE128
], {
top:'1000px',
left:'0px',
width: '100%',
height: '100px',
position: 'static',
frameColor: '#00FF00',
scanbarColor: '#00FF00'
});
scan.onmarked = onmarked;
scan.start();
}
mui.plusReady(function() {
function onmarked(type, result) {
var text = '未知: ';
switch(type) {
case plus.barcode.QR:
text = 'QR: ';
break;
case plus.barcode.EAN13:
text = 'EAN13: ';
break;
case plus.barcode.EAN8:
text = 'EAN8: ';
break;
case plus.barcode.CODE39:
text = 'CODE39: ';
break;
case plus.barcode.CODE93:
text = 'CODE93: ';
break;
case plus.barcode.CODE128:
text = 'CODE128: ';
break;
}
console.log(text + result);
if(result) {
console.log(result)
// scan.close();
// window.location.reload();
scan.cancel();
setTimeout(() => {
scan.start();
},1000)
}
}
mui("body").on('tap', '#scan-go', function() {
mui.openWindow({
url: 'scan_success.html',
id: 'scan_success.html',
styles: { // 窗口参数 参考5+规范中的WebviewStyle,也就是说WebviewStyle下的参数都可以在此设置
titleNView: { // 窗口的标题栏控件
autoBackButton: true,
titleText: '出仓成功', // 标题栏文字,当不设置此属性时,默认加载当前页面的标题,并自动更新页面的标题
titleColor: "#000000", // 字体颜色,颜色值格式为"#RRGGBB",默认值为"#000000"
titleSize: "17px", // 字体大小,默认17px
backgroundColor: "#FFCC00", // 控件背景颜色,颜色值格式为"#RRGGBB",默认值为"#F7F7F7"
}
}
});
})
})
</script>
</body>
</html>
收起阅读 »
5+App升级iOS12系统后应用可能崩溃的解决方案
部分开发者反馈app在iOS12上会崩溃,而同样代码在iOS12以下的手机正常。但也并非所有代码写法都会引发崩溃。
苹果最新的策略在iOS12及以后系统将不再维护UIWebview内核,都推荐改为WKWebview。
目前5+App、wap2app打包时,默认是UIWebview。uni-app默认是WKWebview。
所以5+App、wap2app的开发者,如果遇到iOS12的兼容问题,有如下方案:
1. 将app的iOS webview内核更改为WKWebview
在manifest.json的plus->kernel->ios节点中配置默认使用WKWebviwe内核:
//...
"plus": {
"kernel": {
"ios": "WKWebview"
},
//...
}
注意:
- 目前使用WKWebview内核不支持“启用js原生混淆”功能,云端打包iOS时不要勾选此项配置。
- wgt资源加密要注意,iOS一旦使用WKWebview也不支持解压加密的wgt资源。
- WKWebview里HTML5的存储如cookie、localstorage等和UIWebview不互通,应用升级后,手机端老的cookie等数据拿不到了。
- WKWebview不支持overrideresource。
2. 优化代码写法,避免崩溃
如果代码里有容易引发内存占用且没有回收的代码,比如不停运行的计时器,在UIWebview下更容易崩溃。
在js里不停大范围修改整体dom引发不停重布局,也容易崩溃。
如果配置为WKWebviw内核还存在闪退问题,请提供出现问题的ipa,并详细说明操作出现闪退的步骤。
另,由于一个三方库和iOS12的模拟器冲突了,HBuilde早期版本在真机运行到iOS12模拟器会无法启动,请升级到最新版。此问题不影响真机。
部分开发者反馈app在iOS12上会崩溃,而同样代码在iOS12以下的手机正常。但也并非所有代码写法都会引发崩溃。
苹果最新的策略在iOS12及以后系统将不再维护UIWebview内核,都推荐改为WKWebview。
目前5+App、wap2app打包时,默认是UIWebview。uni-app默认是WKWebview。
所以5+App、wap2app的开发者,如果遇到iOS12的兼容问题,有如下方案:
1. 将app的iOS webview内核更改为WKWebview
在manifest.json的plus->kernel->ios节点中配置默认使用WKWebviwe内核:
//...
"plus": {
"kernel": {
"ios": "WKWebview"
},
//...
}
注意:
- 目前使用WKWebview内核不支持“启用js原生混淆”功能,云端打包iOS时不要勾选此项配置。
- wgt资源加密要注意,iOS一旦使用WKWebview也不支持解压加密的wgt资源。
- WKWebview里HTML5的存储如cookie、localstorage等和UIWebview不互通,应用升级后,手机端老的cookie等数据拿不到了。
- WKWebview不支持overrideresource。
2. 优化代码写法,避免崩溃
如果代码里有容易引发内存占用且没有回收的代码,比如不停运行的计时器,在UIWebview下更容易崩溃。
在js里不停大范围修改整体dom引发不停重布局,也容易崩溃。
如果配置为WKWebviw内核还存在闪退问题,请提供出现问题的ipa,并详细说明操作出现闪退的步骤。
另,由于一个三方库和iOS12的模拟器冲突了,HBuilde早期版本在真机运行到iOS12模拟器会无法启动,请升级到最新版。此问题不影响真机。
收起阅读 »
uni-app 中清除定时器
无论是获取短信码,还是在活动页轮询获取当前活动最新信息,都需要用到定时器。
但是,定时器如果不及时合理地清除,会造成业务逻辑混乱甚至应用卡死的情况。
uni-app 中在某个页面中启动定时器后,一定要在页面关闭时将定时器清除掉。即在页面卸载(关闭)的生命周期函数里,清除定时器。
onUnload:function(){
if(this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
附件中提供了简单的 demo,下载后直接在 HBuilderX 中运行即可。
无论是获取短信码,还是在活动页轮询获取当前活动最新信息,都需要用到定时器。
但是,定时器如果不及时合理地清除,会造成业务逻辑混乱甚至应用卡死的情况。
uni-app 中在某个页面中启动定时器后,一定要在页面关闭时将定时器清除掉。即在页面卸载(关闭)的生命周期函数里,清除定时器。
onUnload:function(){
if(this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
附件中提供了简单的 demo,下载后直接在 HBuilderX 中运行即可。
收起阅读 »
android开发之仿商城首页Banner图的实现
实现效果:当banner滚动的时候 首先会缩放当前以及上一个或下一个banner图,当banner滚动时会,背景会随滚动系数变化缩放(自动滚动),下面相关技术人员来分享一下源码:
//0无状态,1缩放,2放大,3不能再播放动画
private int status = 0;
private void fada() {
if (status == 0 || status == 1) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 0.9f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 0.9f, 1f);
animatorSetsuofang.setDuration(500);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
stopAutoPlay();
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
Log.e("as", "sa");
startAutoPlay();
status = 2;
}
});
animatorSetsuofang.start();
}
}
private void suoxia() {
if (status == 0 || status == 2) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 1f, 0.9f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 1f, 0.9f);
animatorSetsuofang.setDuration(200);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
stopAutoPlay();
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
// startAutoPlay();
status = 1;
}
});
animatorSetsuofang.start();
}
}
动画联动效果实现:
private final Runnable task = new Runnable() {
@Override
public void run() {
if (status == 0 || status == 2) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 1f, 0.9f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 1f, 0.9f);
animatorSetsuofang.setDuration(300);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
if (count > 1 && isAutoPlay) {
currentItem = currentItem % (count + 1) + 1;
// Log.i(tag, "curr:" + currentItem + " count:" + count);
if (currentItem == 1) {
viewPager.setCurrentItem(currentItem, false);
handler.post(task);
} else {
viewPager.setCurrentItem(currentItem);
handler.postDelayed(task, delayTime);
}
}
status = 1;
}
});
animatorSetsuofang.start();
}
}
};
缩放(拖动)效果实现:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int num = 0;
if (mOnPageChangeListener != null) {
// Log.e("as", "position" + position + " ----positionOffset " + positionOffset + " positionOffsetPixels" + positionOffsetPixels);
// Log.e(scale);
Log.e("as", imageUrls.size() + "" + " currentItem" + currentItem);
if (positionOffset > 0) {
Log.e("as", "position" + position + " currentItem" + currentItem + "mViewPagerIndex" + mViewPagerIndex);
// Log.e("as", mViewPagerIndex + "");
if (touch) {
if (currentItem == imageUrls.size() + 1) {
Glide.with(context).load(imageUrls.get(0)).into(roundImageView);
Glide.with(context).load(imageUrls.get(0)).into(roundImagetwo);
} else {
if (currentItem == 0) {
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImagetwo);
} else {
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImagetwo);
}
}
if (position == mViewPagerIndex) {
Log.e("tt", "左");
if (position == imageUrls.size()) {
num = 0;
} else {
num = position;
}
Glide.with(context).load(imageUrls.get(num)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImageView.setVisibility(VISIBLE);
roundImagetwo.setVisibility(INVISIBLE);
} else {
Log.e("tt", "右");
if (position == 0) {
num = imageUrls.size();
} else {
num = position;
}
Glide.with(context).load(imageUrls.get(num - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImagetwo.setVisibility(VISIBLE);
roundImageView.setVisibility(INVISIBLE);
}
} else {
if (!touchover) {
if (position < currentItem) {
roundImageView.setVisibility(VISIBLE);
roundImagetwo.setVisibility(INVISIBLE);
}
}
if (position == imageUrls.size()) {
Glide.with(context).load(imageUrls.get(0)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else if (position == 0) {
num = imageUrls.size();
Glide.with(context).load(imageUrls.get(num - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
if (position == mViewPagerIndex) {
Glide.with(context).load(imageUrls.get(position)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
if (!touchover) {
Glide.with(context).load(imageUrls.get(position)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
Glide.with(context).load(imageUrls.get(position - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImageView.setVisibility(INVISIBLE);
roundImagetwo.setVisibility(VISIBLE);
}
//
}
}
}
//
} else {
roundImagetwo.setVisibility(VISIBLE);
roundImageView.setVisibility(VISIBLE);
if (currentItem == imageUrls.size() + 1) {
Glide.with(context).load(imageUrls.get(0)).into(roundImageView);
Glide.with(context).load(imageUrls.get(0)).into(roundImagetwo);
} else {
if (currentItem == 0) {
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImagetwo);
} else {
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImagetwo);
}
}
}
roundImagetwo.setRound(1f - positionOffset);
roundImageView.setRound(positionOffset);
if ((positionOffset == 0) && touch == false) {
fada();
}
mOnPageChangeListener.onPageScrolled(toRealPosition(position), positionOffset, positionOffsetPixels);
}
}
轮播图数据绑定
banner.setImages(imagebgUrls)
.setImageLoader(new GlideImageLoader())
.setImageList(images)
.start();
好了,到这里就算是完后才能了,如果大家需要改banner依赖包的setImageList()方法,不然无法在主线程中添加数据,还存在不理解的地方也是可以留言咨询。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!
实现效果:当banner滚动的时候 首先会缩放当前以及上一个或下一个banner图,当banner滚动时会,背景会随滚动系数变化缩放(自动滚动),下面相关技术人员来分享一下源码:
//0无状态,1缩放,2放大,3不能再播放动画
private int status = 0;
private void fada() {
if (status == 0 || status == 1) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 0.9f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 0.9f, 1f);
animatorSetsuofang.setDuration(500);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
stopAutoPlay();
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
Log.e("as", "sa");
startAutoPlay();
status = 2;
}
});
animatorSetsuofang.start();
}
}
private void suoxia() {
if (status == 0 || status == 2) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 1f, 0.9f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 1f, 0.9f);
animatorSetsuofang.setDuration(200);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
stopAutoPlay();
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
// startAutoPlay();
status = 1;
}
});
animatorSetsuofang.start();
}
}
动画联动效果实现:
private final Runnable task = new Runnable() {
@Override
public void run() {
if (status == 0 || status == 2) {
AnimatorSet animatorSetsuofang = new AnimatorSet();//组合动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(viewPager, "scaleX", 1f, 0.9f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(viewPager, "scaleY", 1f, 0.9f);
animatorSetsuofang.setDuration(300);
animatorSetsuofang.setInterpolator(new LinearInterpolator());
animatorSetsuofang.play(scaleX).with(scaleY);//两个动画同时开始
animatorSetsuofang.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setViewPagerIsScroll(false);
status = 3;
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setViewPagerIsScroll(true);
if (count > 1 && isAutoPlay) {
currentItem = currentItem % (count + 1) + 1;
// Log.i(tag, "curr:" + currentItem + " count:" + count);
if (currentItem == 1) {
viewPager.setCurrentItem(currentItem, false);
handler.post(task);
} else {
viewPager.setCurrentItem(currentItem);
handler.postDelayed(task, delayTime);
}
}
status = 1;
}
});
animatorSetsuofang.start();
}
}
};
缩放(拖动)效果实现:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int num = 0;
if (mOnPageChangeListener != null) {
// Log.e("as", "position" + position + " ----positionOffset " + positionOffset + " positionOffsetPixels" + positionOffsetPixels);
// Log.e(scale);
Log.e("as", imageUrls.size() + "" + " currentItem" + currentItem);
if (positionOffset > 0) {
Log.e("as", "position" + position + " currentItem" + currentItem + "mViewPagerIndex" + mViewPagerIndex);
// Log.e("as", mViewPagerIndex + "");
if (touch) {
if (currentItem == imageUrls.size() + 1) {
Glide.with(context).load(imageUrls.get(0)).into(roundImageView);
Glide.with(context).load(imageUrls.get(0)).into(roundImagetwo);
} else {
if (currentItem == 0) {
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImagetwo);
} else {
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImagetwo);
}
}
if (position == mViewPagerIndex) {
Log.e("tt", "左");
if (position == imageUrls.size()) {
num = 0;
} else {
num = position;
}
Glide.with(context).load(imageUrls.get(num)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImageView.setVisibility(VISIBLE);
roundImagetwo.setVisibility(INVISIBLE);
} else {
Log.e("tt", "右");
if (position == 0) {
num = imageUrls.size();
} else {
num = position;
}
Glide.with(context).load(imageUrls.get(num - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImagetwo.setVisibility(VISIBLE);
roundImageView.setVisibility(INVISIBLE);
}
} else {
if (!touchover) {
if (position < currentItem) {
roundImageView.setVisibility(VISIBLE);
roundImagetwo.setVisibility(INVISIBLE);
}
}
if (position == imageUrls.size()) {
Glide.with(context).load(imageUrls.get(0)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else if (position == 0) {
num = imageUrls.size();
Glide.with(context).load(imageUrls.get(num - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
if (position == mViewPagerIndex) {
Glide.with(context).load(imageUrls.get(position)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
if (!touchover) {
Glide.with(context).load(imageUrls.get(position)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
} else {
Glide.with(context).load(imageUrls.get(position - 1)).diskCacheStrategy(DiskCacheStrategy.ALL).into(mImagebg);
roundImageView.setVisibility(INVISIBLE);
roundImagetwo.setVisibility(VISIBLE);
}
//
}
}
}
//
} else {
roundImagetwo.setVisibility(VISIBLE);
roundImageView.setVisibility(VISIBLE);
if (currentItem == imageUrls.size() + 1) {
Glide.with(context).load(imageUrls.get(0)).into(roundImageView);
Glide.with(context).load(imageUrls.get(0)).into(roundImagetwo);
} else {
if (currentItem == 0) {
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(imageUrls.size() - 1)).into(roundImagetwo);
} else {
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImageView);
Glide.with(context).load(imageUrls.get(currentItem - 1)).into(roundImagetwo);
}
}
}
roundImagetwo.setRound(1f - positionOffset);
roundImageView.setRound(positionOffset);
if ((positionOffset == 0) && touch == false) {
fada();
}
mOnPageChangeListener.onPageScrolled(toRealPosition(position), positionOffset, positionOffsetPixels);
}
}
轮播图数据绑定
banner.setImages(imagebgUrls)
.setImageLoader(new GlideImageLoader())
.setImageList(images)
.start();
好了,到这里就算是完后才能了,如果大家需要改banner依赖包的setImageList()方法,不然无法在主线程中添加数据,还存在不理解的地方也是可以留言咨询。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!

使用uni.navigateBack修改上一个页面值,多页面传参通信解决方案
此类需求大概意思是:A页面进入B页面,B页面返回并传值给A。
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
var prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.setData({
mdata:1
})
uni.navigateBack(); //上一个页面数据无法修改
解决方案:onfire.js 是一个很简单的事件分发的Javascript库(仅仅 0.9kb),简洁实用。github地址:https://github.com/hustcc/onfire.js/blob/master/README_zh.md
可以用于:
- 简单的事件分发;
- 在 react / vue.js / angular 用于跨组件的轻量级实现;
- 事件订阅和发布;
A页面:
onShow:function(){
var that = this;
onfire.on('setAddressInfo',function(addressInfo){
that.addressInfo = addressInfo;
})
},
B页面:
selectText: function(e){
var that = this;
var that_addressInfo ={
location: e.currentTarget.id
}
onfire.fire('setAddressInfo',that_addressInfo);
uni.navigateBack();
}
参考来源:https://juejin.im/post/5907f120b123db3ee48d2a4f
此类需求大概意思是:A页面进入B页面,B页面返回并传值给A。
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
var prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.setData({
mdata:1
})
uni.navigateBack(); //上一个页面数据无法修改
解决方案:onfire.js 是一个很简单的事件分发的Javascript库(仅仅 0.9kb),简洁实用。github地址:https://github.com/hustcc/onfire.js/blob/master/README_zh.md
可以用于:
- 简单的事件分发;
- 在 react / vue.js / angular 用于跨组件的轻量级实现;
- 事件订阅和发布;
A页面:
onShow:function(){
var that = this;
onfire.on('setAddressInfo',function(addressInfo){
that.addressInfo = addressInfo;
})
},
B页面:
selectText: function(e){
var that = this;
var that_addressInfo ={
location: e.currentTarget.id
}
onfire.fire('setAddressInfo',that_addressInfo);
uni.navigateBack();
}
参考来源:https://juejin.im/post/5907f120b123db3ee48d2a4f
收起阅读 »
uni-app中如何使用5+的原生界面控件(包括map、video、livepusher、barcode、nview)
uni-app可以调用plus的api操作扩展能力,这块很简单,在app的条件编译里直接写就好了,也不需要plus ready。
但是HTML5 里有很多原生的可视化控件,包括map、video、livepusher、barcode、nview(包括原生头、原生tab),获取这些对象和操作他们需要有特殊写法。
5+app开发时,我们可以用plus.webview.currentWebview获取当前页面,但uni-app里用法不一样,需要这样取当前显示的webview:
const currentWebview = this.$mp.page.$getAppWebview(); //注意相关操作写在APP-PLUS条件编译下
还有一种写法比较冗余,可以获取页面栈中任意一个webview对象:
var pages = getCurrentPages();
var page = pages[pages.length - 1];
// #ifdef APP-PLUS
var currentWebview = page.$getAppWebview(); //页面栈最顶层就是当前webview
// #endif
注意uni-app不需要像5+App那样等待plus ready,可以直接用。
有了这个currentWebview
,我们就可以做很多事情了,比如:
例子1 创建直播推流
虽然uni-app的nvue页面已经提供了直播组件。但uni-app的vue页面还没有封装,在app里的vue页面使用直播推流就得用这种写法了。(这里指的是推流,如果是拉流,在app侧可直接使用video标签)
var pusher = plus.video.createLivePusher("", {
url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb',
top:'100px',
left:'0px',
width: '100%',
height: '300px',
position: 'static'
});
currentWebview.append(pusher);
在之前的5+app里,可以使用占位div来布局位置,但在uni-app里,因为没有dom,也没有占位id,所以通过js自己设置原生控件的大小和位置。
例子2 自定义扫码
uni-app的扫码api自带的扫码界面无法有效自定义,大家可以在一个自己的页面里放置一块区域显示扫码控件。
var barcode = plus.barcode.create('barcode', [plus.barcode.QR], {
top:'100px',
left:'0px',
width: '300px',
height: '300px',
position: 'static'
});
//此处未演示扫码成功回调的地址设置,实际请参考HTML5Plus API自行处理
//注意扫码区域需为正方形,否则影响扫码识别率
currentWebview.append(barcode);
当然如果使用app-nvue的话,有自带的barcode组件。
例子3 自定义地图
uni-app的原则是vue语法+小程序api。但小程序的api不如plus.map丰富,地图的重度开发者仍然需要plus的map。
uni-app中单独优化了这个地图的获取,通过$getAppMap可直接得到map对象。
然后参考plus.map的api实现更多地图功能。
例子4 给tabbar加个凸起
本例子仅适用于非自定义组件模式
注意tabbar的获取,不是走getCurrentPages
,而是用plus.webview.currentWebview()
var centerButtonOnTab = new plus.nativeObj.View("",{top:'500px',left:'160px',height:'50px',width:'50px',backgroundColor:'#FF0000'});
plus.webview.currentWebview().append(centerButtonOnTab);
这里只是基础演示,大小位置、点击事件,在实际开发中都得自己处理。
插件市场有已经做好的例子:https://ext.dcloud.net.cn/plugin?id=251
如果app端想要高性能的tabbar凸起,建议使用nvue在前端自绘tabbar。
例子5 操作titleNView,给titleNView右上角加个红点
var nTitle = currentWebview.getTitleNView();
nTitle.drawBitmap("static/reddot.png",{}, {top:'3px',left:'340px',width:'4px',height:'4px'}); //具体尺寸在商用时需自行计算。红点图在附件里。
//nview的api非常多,具体参考:http://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
清除刚才绘制的红点
nTitle.reset();
更新:1.5版的HBuilderX已经支持titleNView的button直接设红点了,hello uni-app的模板里运行到app时也能看到相应示例源码。所以本例子实际作用已经过期,此处纯粹演示用。
5+的plus.nativeObj.view,本质是一种类canvas的画布,可以自由的draw内容上去,更新管理也需要自己维护操作。
包括想在原生控件比如视频、地图上加点什么东西,因为HTML的组件是盖不住原生组件的,都可以使用nview来做。
例子6 uni-app可以在vue页面里写web-view组件,这个组件如何用plus api操作?
vue页面里的web-view 组件其实是一个子webview。
在vue里,通过本文开头的代码,先得到当前页面的webview。
然后调用currentWebview.children()方法来获得所有子webview。
因为执行代码的时机不同,可能执行太早会返回空结果。
如果确定页面里有web-view,则可以再延时一下然后再获取一次children。
这样就能拿到这个子webview。
有了子webview对象后,可以用5+丰富的api来进行前进、后退、拦截资源、禁用schema跳转、注入js等各种操作。具体参考https://www.html5plus.org/doc/zh_cn/webview.html
具体请参考web-view组件的文档
uni-app可以调用plus的api操作扩展能力,这块很简单,在app的条件编译里直接写就好了,也不需要plus ready。
但是HTML5 里有很多原生的可视化控件,包括map、video、livepusher、barcode、nview(包括原生头、原生tab),获取这些对象和操作他们需要有特殊写法。
5+app开发时,我们可以用plus.webview.currentWebview获取当前页面,但uni-app里用法不一样,需要这样取当前显示的webview:
const currentWebview = this.$mp.page.$getAppWebview(); //注意相关操作写在APP-PLUS条件编译下
还有一种写法比较冗余,可以获取页面栈中任意一个webview对象:
var pages = getCurrentPages();
var page = pages[pages.length - 1];
// #ifdef APP-PLUS
var currentWebview = page.$getAppWebview(); //页面栈最顶层就是当前webview
// #endif
注意uni-app不需要像5+App那样等待plus ready,可以直接用。
有了这个currentWebview
,我们就可以做很多事情了,比如:
例子1 创建直播推流
虽然uni-app的nvue页面已经提供了直播组件。但uni-app的vue页面还没有封装,在app里的vue页面使用直播推流就得用这种写法了。(这里指的是推流,如果是拉流,在app侧可直接使用video标签)
var pusher = plus.video.createLivePusher("", {
url:'rtmp://testlivesdk.v0.upaiyun.com/live/upyunb',
top:'100px',
left:'0px',
width: '100%',
height: '300px',
position: 'static'
});
currentWebview.append(pusher);
在之前的5+app里,可以使用占位div来布局位置,但在uni-app里,因为没有dom,也没有占位id,所以通过js自己设置原生控件的大小和位置。
例子2 自定义扫码
uni-app的扫码api自带的扫码界面无法有效自定义,大家可以在一个自己的页面里放置一块区域显示扫码控件。
var barcode = plus.barcode.create('barcode', [plus.barcode.QR], {
top:'100px',
left:'0px',
width: '300px',
height: '300px',
position: 'static'
});
//此处未演示扫码成功回调的地址设置,实际请参考HTML5Plus API自行处理
//注意扫码区域需为正方形,否则影响扫码识别率
currentWebview.append(barcode);
当然如果使用app-nvue的话,有自带的barcode组件。
例子3 自定义地图
uni-app的原则是vue语法+小程序api。但小程序的api不如plus.map丰富,地图的重度开发者仍然需要plus的map。
uni-app中单独优化了这个地图的获取,通过$getAppMap可直接得到map对象。
然后参考plus.map的api实现更多地图功能。
例子4 给tabbar加个凸起
本例子仅适用于非自定义组件模式
注意tabbar的获取,不是走getCurrentPages
,而是用plus.webview.currentWebview()
var centerButtonOnTab = new plus.nativeObj.View("",{top:'500px',left:'160px',height:'50px',width:'50px',backgroundColor:'#FF0000'});
plus.webview.currentWebview().append(centerButtonOnTab);
这里只是基础演示,大小位置、点击事件,在实际开发中都得自己处理。
插件市场有已经做好的例子:https://ext.dcloud.net.cn/plugin?id=251
如果app端想要高性能的tabbar凸起,建议使用nvue在前端自绘tabbar。
例子5 操作titleNView,给titleNView右上角加个红点
var nTitle = currentWebview.getTitleNView();
nTitle.drawBitmap("static/reddot.png",{}, {top:'3px',left:'340px',width:'4px',height:'4px'}); //具体尺寸在商用时需自行计算。红点图在附件里。
//nview的api非常多,具体参考:http://www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
清除刚才绘制的红点
nTitle.reset();
更新:1.5版的HBuilderX已经支持titleNView的button直接设红点了,hello uni-app的模板里运行到app时也能看到相应示例源码。所以本例子实际作用已经过期,此处纯粹演示用。
5+的plus.nativeObj.view,本质是一种类canvas的画布,可以自由的draw内容上去,更新管理也需要自己维护操作。
包括想在原生控件比如视频、地图上加点什么东西,因为HTML的组件是盖不住原生组件的,都可以使用nview来做。
例子6 uni-app可以在vue页面里写web-view组件,这个组件如何用plus api操作?
vue页面里的web-view 组件其实是一个子webview。
在vue里,通过本文开头的代码,先得到当前页面的webview。
然后调用currentWebview.children()方法来获得所有子webview。
因为执行代码的时机不同,可能执行太早会返回空结果。
如果确定页面里有web-view,则可以再延时一下然后再获取一次children。
这样就能拿到这个子webview。
有了子webview对象后,可以用5+丰富的api来进行前进、后退、拦截资源、禁用schema跳转、注入js等各种操作。具体参考https://www.html5plus.org/doc/zh_cn/webview.html
具体请参考web-view组件的文档
收起阅读 »
获取webView.getSettings()
获取webView.getSettings():
var Webview = plus.android.importClass("android.webkit.WebView");
var WebSettings = plus.android.importClass("android.webkit.WebSettings");
var wv = plus.android.currentWebview();
var setting = plus.android.invoke(wv, "getSettings");
获取webView.getSettings():
var Webview = plus.android.importClass("android.webkit.WebView");
var WebSettings = plus.android.importClass("android.webkit.WebSettings");
var wv = plus.android.currentWebview();
var setting = plus.android.invoke(wv, "getSettings");
详见博客:native.js设置可缩放的webview并隐藏缩放控件
收起阅读 »
android软件开发之仿淘宝选择规格的实现
在一些app开发项目中选择商品规格这个功能最容易遇到问题,想要实现需要的全部功能,但一直没有成功,所以就去找了个Demo,学习界面UI采用recyclerview,item里面渲染ViewGroup,根据数据源的数量,往ViewGroup里面添加Textview。这样就可以解决它的每个属性按钮宽高自适应。下面来详细分享一下源码:
/**
- 测量子view大小 根据子控件设置宽和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获得它的父容器为它设置的测量模式和大小
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// 如果是warp_content情况下,记录宽和高
int width = 0;
int height = 0;
/**
- 记录每一行的宽度,width不断取最大宽度
*/
int lineWidth = 0;
/**
- 每一行的高度,累加至height
*/
int lineHeight = 0;
int cCount = getChildCount();
// 遍历每个子元素
for (int i = 0; i < cCount; i++)
{
View child = getChildAt(i);
// 测量每一个child的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到child的布局管理器
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
// 当前子空间实际占据的宽度
int childWidth = child.getMeasuredWidth() + lp.leftMargin
- lp.rightMargin;
// 当前子空间实际占据的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin
- lp.bottomMargin;
/**
- 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行
*/
if (lineWidth + childWidth > sizeWidth)
{
width = Math.max(lineWidth, childWidth);// 取最大的
lineWidth = childWidth; // 重新开启新行,开始记录
// 叠加当前高度,
height += lineHeight;
// 开启记录下一行的高度
lineHeight = childHeight;
} else
// 否则累加值lineWidth,lineHeight取最大高度
{
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较
if (i == cCount - 1)
{
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
- setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ?sizeWidth
width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
mAllViews.clear();
mLineHeight.clear();
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
// 存储每一行所有的childView
ListlineViews = new ArrayList<>();
int cCount = getChildCount();
// 遍历所有的孩子
for (int i = 0; i < cCount; i++)
{
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// 如果已经需要换行
if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)
{
// 记录这一行所有的View以及最大高度
mLineHeight.add(lineHeight);
// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
mAllViews.add(lineViews);
lineWidth = 0;// 重置行宽
lineViews = new ArrayList<>();
}
/**
- 如果不需要换行,则累加
*/
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
- lp.bottomMargin);
lineViews.add(child);
}
// 记录最后一行
mLineHeight.add(lineHeight);
mAllViews.add(lineViews);
int left = 0;
int top = 0;
// 得到总行数
int lineNums = mAllViews.size();
for (int i = 0; i < lineNums; i++)
{
// 每一行的所有的views
lineViews = mAllViews.get(i);
// 当前行的最大高度
lineHeight = mLineHeight.get(i);
// 遍历当前行所有的View
for (int j = 0; j < lineViews.size(); j++)
{
View child = lineViews.get(j);
if (child.getVisibility() == View.GONE)
{
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
//计算childView的Marginleft,top,right,bottom
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc =lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc, tc, rc, bc);
left += child.getMeasuredWidth() + lp.rightMargin
- lp.leftMargin;
}
left = 0;
top += lineHeight;
}
}
接下来是SKU的算法,把选项状态(三种:不能选择,可以选择,已选中)依次对属性按钮做出修改,这里虽然做了一些不必要的循环判断,但胜在功能的实现adapter代码(重点initOptions、canClickOptions和getSelected三个方法)
public class GoodsAttrsAdapter extends BaseRecyclerAdapter {
private SKUInterface myInterface;
private SimpleArrayMap saveClick;
private List stockGoodsList;//商品数据集合
private String[] selectedValue; //选中的属性
private TextView[][] childrenViews; //二维 装所有属性
private final int SELECTED = 0x100;
private final int CANCEL = 0x101;
public GoodsAttrsAdapter(Context ctx, List list, List stockGoodsList) {
super(ctx, list);
this.stockGoodsList = stockGoodsList;
saveClick = new SimpleArrayMap<>();
childrenViews = new TextView[list.size()][0];
selectedValue = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
selectedValue[i] = "";
}
}
public void setSKUInterface(SKUInterface myInterface) {
this.myInterface = myInterface;
}
@Override
public int getItemLayoutId(int viewType) {
return R.layout.item_skuattrs;
}
@Override
public void bindData(RecyclerViewHolder holder, int position, GoodsAttrsBean.AttributesBean item) {
TextView tv_ItemName = holder.getTextView(R.id.tv_ItemName);
SKUViewGroup vg_skuItem = (SKUViewGroup) holder.getView(R.id.vg_skuItem);
tv_ItemName.setText(item.getTabName());
Listchildrens = item.getAttributesItem();
int childrenSize = childrens.size();
TextView[] textViews = new TextView[childrenSize];
for (int i = 0; i < childrenSize; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 5, 5, 0);
TextView textView = new TextView(mContext);
textView.setGravity(Gravity.CENTER);
textView.setPadding(15, 5, 15, 5);
textView.setLayoutParams(params);
textView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.saddlebrown));
textView.setText(childrens.get(i));
textView.setTextColor(ContextCompat.getColor(mContext, R.color.white));
textViews[i] = textView;
vg_skuItem.addView(textViews[i]);
}
childrenViews[position] = textViews;
initOptions();
canClickOptions();
getSelected();
}
private int focusPositionG, focusPositionC;
private class MyOnClickListener implements View.OnClickListener {
//点击操作 选中SELECTED 取消CANCEL
private int operation;
private int positionG;
private int positionC;
public MyOnClickListener(int operation, int positionG, int positionC) {
this.operation = operation;
this.positionG = positionG;
this.positionC = positionC;
}
@Override
public void onClick(View v) {
focusPositionG = positionG;
focusPositionC = positionC;
String value = childrenViews[positionG][positionC].getText().toString();
switch (operation) {
case SELECTED:
saveClick.put(positionG, positionC + "");
selectedValue[positionG] = value;
myInterface.selectedAttribute(selectedValue);
break;
case CANCEL:
saveClick.put(positionG, "");
for (int l = 0; l < selectedValue.length; l++) {
if (selectedValue[l].equals(value)) {
selectedValue[l] = "";
break;
}
}
myInterface.uncheckAttribute(selectedValue);
break;
}
initOptions();
canClickOptions();
getSelected();
}
}
class MyOnFocusChangeListener implements View.OnFocusChangeListener {
private int positionG;
private int positionC;
public MyOnFocusChangeListener(int positionG, int positionC) {
this.positionG = positionG;
this.positionC = positionC;
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
String clickpositionC = saveClick.get(positionG);
if (hasFocus) {
v.setBackgroundColor(ContextCompat.getColor(mContext, R.color.pink));
if (TextUtils.isEmpty(clickpositionC)) {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
} else if (clickpositionC.equals(positionC + "")) {
} else {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
}
} else {
v.setBackgroundColor(ContextCompat.getColor(mContext, R.color.saddlebrown));
if (TextUtils.isEmpty(clickpositionC)) {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.white));
} else if (clickpositionC.equals(positionC + "")) {
} else {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.white));
}
}
}
}
/**
- 初始化选项(不可点击,焦点消失)
*/
private void initOptions() {
for (int y = 0; y < childrenViews.length; y++) {
for (int z = 0; z < childrenViews[y].length; z++) {//循环所有属性
TextView textView = childrenViews[y][z];
textView.setEnabled(false);
textView.setFocusable(false);
textView.setTextColor(ContextCompat.getColor(mContext, R.color.gray));//变灰
}
}
}
/**
- 找到符合条件的选项变为可选
*/
private void canClickOptions() {
for (int i = 0; i < childrenViews.length; i++) {
for (int j = 0; j < stockGoodsList.size(); j++) {
boolean filter = false;
List goodsInfo = stockGoodsList.get(j).getGoodsInfo();
for (int k = 0; k < selectedValue.length; k++) {
if (i == k || TextUtils.isEmpty(selectedValue[k])) {
continue;
}
if (!selectedValue[k].equals(goodsInfo
.get(k).getTabValue())) {
filter = true;
break;
}
}
if (!filter) {
for (int n = 0; n < childrenViews[i].length; n++) {
TextView textView = childrenViews[i][n];//拿到所有属性TextView
String name = textView.getText().toString();
//拿到属性名称
if (goodsInfo.get(i).getTabValue().equals(name)) {
textView.setEnabled(true);//符合就变成可点击
textView.setFocusable(true); //设置可以获取焦点
//不要让焦点乱跑
if (focusPositionG == i && focusPositionC == n) {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
textView.requestFocus();
} else {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.white));
}
textView.setOnClickListener(new MyOnClickListener(SELECTED, i, n) {
});
textView.setOnFocusChangeListener(new MyOnFocusChangeListener(i, n) {
});
}
}
}
}
}
}
/**
- 找到已经选中的选项,让其变红
*/
private void getSelected() {
for (int i = 0; i < childrenViews.length; i++) {
for (int j = 0; j < childrenViews[i].length; j++) {//拿到每行属性Item
TextView textView = childrenViews[i][j];//拿到所有属性TextView
String value = textView.getText().toString();
for (int m = 0; m < selectedValue.length; m++) {
if (selectedValue[m].equals(value)) {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.red));
textView.setOnClickListener(new MyOnClickListener(CANCEL, i, j) {
});
}
}
}
}
}
}
好了,到这里就算是结束了,如果还是存在有疑问的地方,大家可以留言咨询。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!
在一些app开发项目中选择商品规格这个功能最容易遇到问题,想要实现需要的全部功能,但一直没有成功,所以就去找了个Demo,学习界面UI采用recyclerview,item里面渲染ViewGroup,根据数据源的数量,往ViewGroup里面添加Textview。这样就可以解决它的每个属性按钮宽高自适应。下面来详细分享一下源码:
/**
- 测量子view大小 根据子控件设置宽和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获得它的父容器为它设置的测量模式和大小
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// 如果是warp_content情况下,记录宽和高
int width = 0;
int height = 0;
/**
- 记录每一行的宽度,width不断取最大宽度
*/
int lineWidth = 0;
/**
- 每一行的高度,累加至height
*/
int lineHeight = 0;
int cCount = getChildCount();
// 遍历每个子元素
for (int i = 0; i < cCount; i++)
{
View child = getChildAt(i);
// 测量每一个child的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到child的布局管理器
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
// 当前子空间实际占据的宽度
int childWidth = child.getMeasuredWidth() + lp.leftMargin
- lp.rightMargin;
// 当前子空间实际占据的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin
- lp.bottomMargin;
/**
- 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行
*/
if (lineWidth + childWidth > sizeWidth)
{
width = Math.max(lineWidth, childWidth);// 取最大的
lineWidth = childWidth; // 重新开启新行,开始记录
// 叠加当前高度,
height += lineHeight;
// 开启记录下一行的高度
lineHeight = childHeight;
} else
// 否则累加值lineWidth,lineHeight取最大高度
{
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较
if (i == cCount - 1)
{
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
- setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ?sizeWidth
width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
mAllViews.clear();
mLineHeight.clear();
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
// 存储每一行所有的childView
ListlineViews = new ArrayList<>();
int cCount = getChildCount();
// 遍历所有的孩子
for (int i = 0; i < cCount; i++)
{
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// 如果已经需要换行
if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)
{
// 记录这一行所有的View以及最大高度
mLineHeight.add(lineHeight);
// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
mAllViews.add(lineViews);
lineWidth = 0;// 重置行宽
lineViews = new ArrayList<>();
}
/**
- 如果不需要换行,则累加
*/
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
- lp.bottomMargin);
lineViews.add(child);
}
// 记录最后一行
mLineHeight.add(lineHeight);
mAllViews.add(lineViews);
int left = 0;
int top = 0;
// 得到总行数
int lineNums = mAllViews.size();
for (int i = 0; i < lineNums; i++)
{
// 每一行的所有的views
lineViews = mAllViews.get(i);
// 当前行的最大高度
lineHeight = mLineHeight.get(i);
// 遍历当前行所有的View
for (int j = 0; j < lineViews.size(); j++)
{
View child = lineViews.get(j);
if (child.getVisibility() == View.GONE)
{
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
//计算childView的Marginleft,top,right,bottom
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc =lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc, tc, rc, bc);
left += child.getMeasuredWidth() + lp.rightMargin
- lp.leftMargin;
}
left = 0;
top += lineHeight;
}
}
接下来是SKU的算法,把选项状态(三种:不能选择,可以选择,已选中)依次对属性按钮做出修改,这里虽然做了一些不必要的循环判断,但胜在功能的实现adapter代码(重点initOptions、canClickOptions和getSelected三个方法)
public class GoodsAttrsAdapter extends BaseRecyclerAdapter {
private SKUInterface myInterface;
private SimpleArrayMap saveClick;
private List stockGoodsList;//商品数据集合
private String[] selectedValue; //选中的属性
private TextView[][] childrenViews; //二维 装所有属性
private final int SELECTED = 0x100;
private final int CANCEL = 0x101;
public GoodsAttrsAdapter(Context ctx, List list, List stockGoodsList) {
super(ctx, list);
this.stockGoodsList = stockGoodsList;
saveClick = new SimpleArrayMap<>();
childrenViews = new TextView[list.size()][0];
selectedValue = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
selectedValue[i] = "";
}
}
public void setSKUInterface(SKUInterface myInterface) {
this.myInterface = myInterface;
}
@Override
public int getItemLayoutId(int viewType) {
return R.layout.item_skuattrs;
}
@Override
public void bindData(RecyclerViewHolder holder, int position, GoodsAttrsBean.AttributesBean item) {
TextView tv_ItemName = holder.getTextView(R.id.tv_ItemName);
SKUViewGroup vg_skuItem = (SKUViewGroup) holder.getView(R.id.vg_skuItem);
tv_ItemName.setText(item.getTabName());
Listchildrens = item.getAttributesItem();
int childrenSize = childrens.size();
TextView[] textViews = new TextView[childrenSize];
for (int i = 0; i < childrenSize; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(5, 5, 5, 0);
TextView textView = new TextView(mContext);
textView.setGravity(Gravity.CENTER);
textView.setPadding(15, 5, 15, 5);
textView.setLayoutParams(params);
textView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.saddlebrown));
textView.setText(childrens.get(i));
textView.setTextColor(ContextCompat.getColor(mContext, R.color.white));
textViews[i] = textView;
vg_skuItem.addView(textViews[i]);
}
childrenViews[position] = textViews;
initOptions();
canClickOptions();
getSelected();
}
private int focusPositionG, focusPositionC;
private class MyOnClickListener implements View.OnClickListener {
//点击操作 选中SELECTED 取消CANCEL
private int operation;
private int positionG;
private int positionC;
public MyOnClickListener(int operation, int positionG, int positionC) {
this.operation = operation;
this.positionG = positionG;
this.positionC = positionC;
}
@Override
public void onClick(View v) {
focusPositionG = positionG;
focusPositionC = positionC;
String value = childrenViews[positionG][positionC].getText().toString();
switch (operation) {
case SELECTED:
saveClick.put(positionG, positionC + "");
selectedValue[positionG] = value;
myInterface.selectedAttribute(selectedValue);
break;
case CANCEL:
saveClick.put(positionG, "");
for (int l = 0; l < selectedValue.length; l++) {
if (selectedValue[l].equals(value)) {
selectedValue[l] = "";
break;
}
}
myInterface.uncheckAttribute(selectedValue);
break;
}
initOptions();
canClickOptions();
getSelected();
}
}
class MyOnFocusChangeListener implements View.OnFocusChangeListener {
private int positionG;
private int positionC;
public MyOnFocusChangeListener(int positionG, int positionC) {
this.positionG = positionG;
this.positionC = positionC;
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
String clickpositionC = saveClick.get(positionG);
if (hasFocus) {
v.setBackgroundColor(ContextCompat.getColor(mContext, R.color.pink));
if (TextUtils.isEmpty(clickpositionC)) {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
} else if (clickpositionC.equals(positionC + "")) {
} else {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
}
} else {
v.setBackgroundColor(ContextCompat.getColor(mContext, R.color.saddlebrown));
if (TextUtils.isEmpty(clickpositionC)) {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.white));
} else if (clickpositionC.equals(positionC + "")) {
} else {
((TextView) v).setTextColor(ContextCompat.getColor(mContext, R.color.white));
}
}
}
}
/**
- 初始化选项(不可点击,焦点消失)
*/
private void initOptions() {
for (int y = 0; y < childrenViews.length; y++) {
for (int z = 0; z < childrenViews[y].length; z++) {//循环所有属性
TextView textView = childrenViews[y][z];
textView.setEnabled(false);
textView.setFocusable(false);
textView.setTextColor(ContextCompat.getColor(mContext, R.color.gray));//变灰
}
}
}
/**
- 找到符合条件的选项变为可选
*/
private void canClickOptions() {
for (int i = 0; i < childrenViews.length; i++) {
for (int j = 0; j < stockGoodsList.size(); j++) {
boolean filter = false;
List goodsInfo = stockGoodsList.get(j).getGoodsInfo();
for (int k = 0; k < selectedValue.length; k++) {
if (i == k || TextUtils.isEmpty(selectedValue[k])) {
continue;
}
if (!selectedValue[k].equals(goodsInfo
.get(k).getTabValue())) {
filter = true;
break;
}
}
if (!filter) {
for (int n = 0; n < childrenViews[i].length; n++) {
TextView textView = childrenViews[i][n];//拿到所有属性TextView
String name = textView.getText().toString();
//拿到属性名称
if (goodsInfo.get(i).getTabValue().equals(name)) {
textView.setEnabled(true);//符合就变成可点击
textView.setFocusable(true); //设置可以获取焦点
//不要让焦点乱跑
if (focusPositionG == i && focusPositionC == n) {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.dodgerblue));
textView.requestFocus();
} else {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.white));
}
textView.setOnClickListener(new MyOnClickListener(SELECTED, i, n) {
});
textView.setOnFocusChangeListener(new MyOnFocusChangeListener(i, n) {
});
}
}
}
}
}
}
/**
- 找到已经选中的选项,让其变红
*/
private void getSelected() {
for (int i = 0; i < childrenViews.length; i++) {
for (int j = 0; j < childrenViews[i].length; j++) {//拿到每行属性Item
TextView textView = childrenViews[i][j];//拿到所有属性TextView
String value = textView.getText().toString();
for (int m = 0; m < selectedValue.length; m++) {
if (selectedValue[m].equals(value)) {
textView.setTextColor(ContextCompat.getColor(mContext, R.color.red));
textView.setOnClickListener(new MyOnClickListener(CANCEL, i, j) {
});
}
}
}
}
}
}
好了,到这里就算是结束了,如果还是存在有疑问的地方,大家可以留言咨询。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!

设置headers 报错Request header field Content-Type is not allowed by Access-Control-Allow-Headers
我要自定义一些参数放在headers里面,试了好多方法都不行
比如:http://ask.dcloud.net.cn/question/8596这里的很多个方式
http://ask.dcloud.net.cn/article/13026的方式
后来比较明确的报错Request header field “我自定义的key” is not allowed by Access-Control-Allow-Headers in preflight response.
又去搜了一下找到https://www.cnblogs.com/caimuqing/p/6733405.html,结果发现是后端需要把要传到后端的headers里面的key加入到response
// TODO 支持跨域访问
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");//这里“Access-Token”是我要传到后台的内容key
response.setHeader("Access-Control-Expose-Headers", "*");
if (request.getMethod().equals("OPTIONS")) {
HttpUtil.setResponse(response, HttpStatus.OK.value(), null);
return;
}
共勉给想要自定义headers的同学
我要自定义一些参数放在headers里面,试了好多方法都不行
比如:http://ask.dcloud.net.cn/question/8596这里的很多个方式
http://ask.dcloud.net.cn/article/13026的方式
后来比较明确的报错Request header field “我自定义的key” is not allowed by Access-Control-Allow-Headers in preflight response.
又去搜了一下找到https://www.cnblogs.com/caimuqing/p/6733405.html,结果发现是后端需要把要传到后端的headers里面的key加入到response
// TODO 支持跨域访问
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Allow-Headers", "Content-Type,Access-Token");//这里“Access-Token”是我要传到后台的内容key
response.setHeader("Access-Control-Expose-Headers", "*");
if (request.getMethod().equals("OPTIONS")) {
HttpUtil.setResponse(response, HttpStatus.OK.value(), null);
return;
}
共勉给想要自定义headers的同学
收起阅读 »
微信公众号开发之客服功能的群发消息功能
本周我们做了一个有技术含量的一个新功能:客服功能的群发消息功能。此功能主要用于微信公众号客服群发提醒,比如客户的商家认证审核通过,用户购买商品成功等功能提醒,可以有效减少网站项目开发所必要的资金节约。
- 接口代码。
接口代码如下:
public function reply_customer(){
$mtime=strtotime("-1 hour");
$where['add_time']=array("gt",$mtime);
$res=M("fa_need")->where($where)->order("id desc")->find();
$id=$res['id'];
$new_time=time();
$go_time=$res['add_time']+$res['show_time']*60;
if($new_time<$go_time){
$credit_level=$res['credit_level'];
$ya_money=$res['ya_money'];
$wh['credit_level']=array("egt",$credit_level);
$wh['ya_money']=array("egt",$ya_money);
$wh['ya_money']=array("neq",$ya_money);
$wh['jie_status']=3;
$list=M("user")->where($wh)->limit(199)->select();
foreach ($list as $key => $value) {
$info[]=$value['openid'];
}
$touser=I('touser');
$content="亲,有新任务哦,可以点击链接接单啦!";
//更换成自己的APPID和APPSECRET
$APPID="wx4ae938a141e9193a";
$APPSECRET="d0ef15664f42de92875f86b8f9f98edf";
$TOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$APPID."&secret=".$APPSECRET;
$json=file_get_contents($TOKEN_URL);
$result=json_decode($json);
$ACC_TOKEN=$result->access_token;
foreach($info as $val){
$data = '{
"touser":"'.$val.'",
"msgtype":"text",
"text":
{
"content":"'.$content.'http://www.xxx.com/Task/task_details?id='.$id.'"
}
}';
$url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=".$ACC_TOKEN;
$result = $this->https_post($url,$data);
$final = json_decode($result);
echo $final;
}
}
}
public function https_post($url,$data)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
if (curl_errno($curl)) {
return 'Errno'.curl_error($curl);
}
curl_close($curl);
return $result;
}
- php调用接口代码
Php代码如下:
public function fa_need(){
vendor('Weixinup.jssdk');
$jssdk = new JSSDK("wx4ae938a141e9193a", "d0ef15664f42de92875f86b8f9f98edf");
$signPackage = $jssdk->GetSignPackage();
// var_dump($signPackage);die;
$this->assign('signPackage', $signPackage);
$uid=session('uid');
$model=M('user');
$list=$model->where("uid=$uid")->find();
$mod=M("school");
$school=$mod->select();
$this->reply_customer();
// if (I('id')!=""||!empty(I('id'))) {
// $faid["id"] = I('id');
// $res = M("fa_need")->where($faid)->find();
// $this->assign("fa_needinfo",$res);
// }
$this->assign("school",$school);
$this->assign("list",$list);
$this->display();
}
好了,到这里大家应该清楚是如何实现的吧,那么如果还是存在有不理解的地方,可以留言咨询获取帮助解答。
本文由专业的郑州小程序开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!
本周我们做了一个有技术含量的一个新功能:客服功能的群发消息功能。此功能主要用于微信公众号客服群发提醒,比如客户的商家认证审核通过,用户购买商品成功等功能提醒,可以有效减少网站项目开发所必要的资金节约。
- 接口代码。
接口代码如下:
public function reply_customer(){
$mtime=strtotime("-1 hour");
$where['add_time']=array("gt",$mtime);
$res=M("fa_need")->where($where)->order("id desc")->find();
$id=$res['id'];
$new_time=time();
$go_time=$res['add_time']+$res['show_time']*60;
if($new_time<$go_time){
$credit_level=$res['credit_level'];
$ya_money=$res['ya_money'];
$wh['credit_level']=array("egt",$credit_level);
$wh['ya_money']=array("egt",$ya_money);
$wh['ya_money']=array("neq",$ya_money);
$wh['jie_status']=3;
$list=M("user")->where($wh)->limit(199)->select();
foreach ($list as $key => $value) {
$info[]=$value['openid'];
}
$touser=I('touser');
$content="亲,有新任务哦,可以点击链接接单啦!";
//更换成自己的APPID和APPSECRET
$APPID="wx4ae938a141e9193a";
$APPSECRET="d0ef15664f42de92875f86b8f9f98edf";
$TOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$APPID."&secret=".$APPSECRET;
$json=file_get_contents($TOKEN_URL);
$result=json_decode($json);
$ACC_TOKEN=$result->access_token;
foreach($info as $val){
$data = '{
"touser":"'.$val.'",
"msgtype":"text",
"text":
{
"content":"'.$content.'http://www.xxx.com/Task/task_details?id='.$id.'"
}
}';
$url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=".$ACC_TOKEN;
$result = $this->https_post($url,$data);
$final = json_decode($result);
echo $final;
}
}
}
public function https_post($url,$data)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
if (curl_errno($curl)) {
return 'Errno'.curl_error($curl);
}
curl_close($curl);
return $result;
}
- php调用接口代码
Php代码如下:
public function fa_need(){
vendor('Weixinup.jssdk');
$jssdk = new JSSDK("wx4ae938a141e9193a", "d0ef15664f42de92875f86b8f9f98edf");
$signPackage = $jssdk->GetSignPackage();
// var_dump($signPackage);die;
$this->assign('signPackage', $signPackage);
$uid=session('uid');
$model=M('user');
$list=$model->where("uid=$uid")->find();
$mod=M("school");
$school=$mod->select();
$this->reply_customer();
// if (I('id')!=""||!empty(I('id'))) {
// $faid["id"] = I('id');
// $res = M("fa_need")->where($faid)->find();
// $this->assign("fa_needinfo",$res);
// }
$this->assign("school",$school);
$this->assign("list",$list);
$this->display();
}
好了,到这里大家应该清楚是如何实现的吧,那么如果还是存在有不理解的地方,可以留言咨询获取帮助解答。
本文由专业的郑州小程序开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!