这是一款纯鸿蒙版的哔哩哔哩服务卡片应用。
6月2日鸿蒙发布,今年的六月已经被鸿蒙刷屏了。从安卓到鸿蒙,最直观的变化应该就是服务卡片了。我也是在学习鸿蒙的同时,实际体验一下服务卡片的开发。 给大家看看最终的效果。
接下来分享下我的制作过程,我使用的开发环境是 IDE:DevEco Studio 2.1 Release SDK:API Version 5
软件安装和项目建立的部分就跳过了,相信大家都比较熟悉了。直奔主题服务卡片的制作。
首先要先了解服务卡片,都有哪些尺寸,支持哪些组件,使用什么语言。然后规划好要实现哪些功能。
服务卡片有4种尺寸,分别是微卡片、小卡片、中卡片、大卡片。官方提供了4种基础模板,12种高级模板。可以选择。基础模板如下图
服务卡片设计的初衷就是信息显示、服务直达。依照这个原则,我找了几个Bilibili中我比较常用的功能,来制作服务卡片,比如追番列表。
3.开发语言
看下表就一目了然了,就是推荐JS。文档中心
本着学习的目的,卡片界面就不使用模板了。不过我们还是要通过IDE>>File>>New>>Service Widget来添加服务卡片,这样添加IDE会自动添加配置和管理相关文件。然后服务卡片的界面重新编写。服务卡片常用的的容器组件有div、list、stack、swiper等。我使用了4种尺寸的卡片,并尽可能的使用到所有的容器组件。
就是用来划分区域的。比较常用。比如追番服务卡片。效果如图,代码如下
<div class="div_root" ><!--在服务卡片设置一个 根div 横向布局-->
<div class="div_container"><!--在根div 横向放置4个div,每个div内部从上往下排列-->
<image class="item_image" src="{{ src1 }}" onclick="routerEvent1"></image>
<text class="item_title">{{ itemTitle1 }}</text>
<text class="item_content">{{ itemContent1 }}</text>
</div>
<div class="div_container"><!--第二列-->
<image class="item_image" src="{{ src2 }}" onclick="routerEvent2"></image>
<text class="item_title">{{ itemTitle2 }}</text>
<text class="item_content">{{ itemContent2 }}</text>
</div>
<div class="div_container"><!--第三列-->
<image class="item_image" src="{{ src3 }}" onclick="routerEvent3"></image>
<text class="item_title">{{ itemTitle3 }}</text>
<text class="item_content">{{ itemContent3 }}</text>
</div>
<div class="div_container"><!--第四列-->
<image class="item_image" src="{{ src4 }}" onclick="routerEvent4"></image>
<text class="item_title">{{ itemTitle4 }}</text>
<text class="item_content">{{ itemContent4 }}</text>
</div>
</div>
.div_root {
flex-direction: row; /*flex容器主轴方向,row:水平方向从左到右。*/
justify-content: center; /*flex容器当前行的主轴对齐格式,center:项目位于容器的中心。*/
margin:6px; /*外边距属性:只有一个值时,这个值会被指定给全部的四个边。*/
border-radius: 10px; /*设置元素的外边框圆角半径。*/
}
.div_container {
flex-direction: column; /*flex容器主轴方向,column:垂直方向从上到下。*/
justify-content: flex-start; /*flex容器当前行的主轴对齐格式,flex-start:项目位于容器的开头。*/
margin:6px;
}
.item_image {
height: 60%; /*卡片在不同设备,尺寸会发生变化,所以最好使用百分比进行标注。*/
border-radius: 10px;
background-color: #F1F3F5; /*设置背景颜色。*/
}
@media (dark-mode: true) { /*当前系统为深色模式时,使用这里的配置,如果没有颜色设置,可以不设置*/
.item_image {
height: 60%;
border-radius: 10px;
background-color: #202224;
}
}
.item_title {
margin-top: 10px; /*设置上边距。*/
font-size: 12px; /*设置文本的尺寸。*/
font-weight: bold; /*设置文本的字体粗细。取值[100, 900],默认为400。*/
max-lines:1; /*设置文本的最大行数。*/
text-overflow: ellipsis; /*根据父容器大小显示,显示不下的文本用省略号代替。需配合max-lines使用。*/
color: #e5000000; /*设置文本的颜色。*/
}
.item_content {
margin-top: 5px;
font-size: 9px;
font-weight: bold;
text-overflow: ellipsis;
max-lines:1;
color: #99000000;
}
Note:其实这个服务卡片的布局,每一列的内容都是相同的,是应该使用list组件的。
就如上面所说的连续相同的部分,可以使用这个组件,List不但可以显示更多的内容,而且代码更少。效果图如下
<list class="list">
<list-item for="{{cards}}" class="list-item">
<div class="div" onclick="sendRouteEvent">
<image class="item_image" src="{{ $item.pic }}"></image>
<text class="item_name">{{ $item.name }}</text>
<text class="item_title">{{ $item.title }}</text>
</div>
</list-item>
</list>
.list{
align-items:center; /*list每一列交叉轴上的对齐格式:元素在交叉轴居中*/
}
.list-item{
border-radius: 15px;
background-color: #f2f2f2;
margin-bottom: 5px;
}
.div{
flex-direction: column;
}
.item_image {
border-top-right-radius: 15px;
border-top-left-radius: 15px;
}
.item_name {
margin:5px 8px 0px;
font-size: 12px;
color: #262626;
}
.item_title{
margin:3px 8px 8px;
font-size: 10px;
color: #AAAAAA;
max-lines: 2;
text-overflow: ellipsis; /* 省略号 */
}
简单来说就是可以在一张图片上堆叠显示另一张图片,例如下图蓝框的图片覆盖在红框图片的上面。
正常情况下swiper是可以实现上下、左右滑动操作的。但是放置在桌面上的服务卡片,在左右滑动操作的时候,会使系统分不清楚用户是要左右滑动屏幕,还是左右滑动卡片。所以目前服务卡片的swiper容器是不支持手势滑动切换子组件的。下图是通过点击图片侧面的控制条实现上下滑动的。但是我个人觉得上下滑动其实还是挺好用的,毕竟在list组件上是可以上下滑动的,只可惜目前还不支持。
总结:服务卡片的设计比较简单,零基础也没关系,官方还贴心的准备了模板。只要挑选模板,设置变量也能快速构建。
卡片设计好之后,就需要通过Bilibili的API来获取数据了。主要就是给权限添加依赖,然后发送网络请求,通过API获取JSON的返回值,然后解析JSON得到我们需要的数据。
要在config.json配置文件的module中添加:“reqPermissions”: [{“name”:“ohos.permission.INTERNET”}],
{
... ...
"module": {
... ...
"reqPermissions": [{"name":"ohos.permission.INTERNET"}]
}
}
找到entry/build.gradle文件,在dependencies下添加
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
testImplementation 'junit:junit:4.13'
ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100'
// ZZRHttp 可以单独一个进程进行http请求
implementation 'com.zzrv5.zzrhttp:ZZRHttp:1.0.1'
// fastjson 可以解析JSON格式
implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.75'
}
以获取粉丝数为例。如果在浏览器中输入 https://api.bilibili.com/x/relation/stat?vmid=383565952 (其中vmid:是要查询的用户ID)
follower的值就是粉丝数。
网络访问我们可以使用HttpURLConnection,或者okhttp等依赖包,但是需要开启子线程、处理异常等操作,所以这里使用的是ZZR老师封装好的ZZRHttp
代码实现:
//获取Bilibili粉丝数,这里就要用到第二步我们添加的ZZRHttp
String url="https://api.bilibili.com/x/relation/stat?vmid=383565952";
ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {
HiLog.info(TAG, "API返回失败");
}
@Override
public void onResponse(String s) {
HiLog.info(TAG, "API返回成功");
// 如果返回成功,返回的结果就会保存在 String s 中。
// s={"code":0,"message":"0","ttl":1,"data":{"mid":383565952,"following":70,"whisper":0,"black":0,"follower":5384}}
}
});
得到的是JSON格式的返回值,要得到follower的值,还需要对JSON进行数据解析。
先按照JSON的内容,生成JAVA类。代码如下。可以自己写,也可以百度搜 ”JSON生成Java实体类“,可直接生成。
public class BilibiliFollower {
public static class Data{
private int follower;
public int getFollower() {
return follower;
}
public void setFollower(int follower) {
this.follower=follower;
}
}
private BilibiliFollower.Data data;
public BilibiliFollower.Data getData() {
return data;
}
public void setData(BilibiliFollower.Data data) {
this.data=data;
}
}
//解析JSON,使用第二步我们添加的fastjson包
try {
//1.调用fastjson解析,结果保存在JSON对应的类
BilibiliFollower bilibiliFollower=JSON.parseObject(s,BilibiliFollower.class);
//2.get方法获取解析内容
BilibiliFollower.Data data=bilibiliFollower.getData();
System.out.println("解析成功"+data.getFollower());
} catch (Exception e) {
HiLog.info(TAG, "解析失败");
}
总结:一定要添加联网权限不然是获取不到数据的。添加了2个依赖包,可以很方便的提取数据。获取其他的卡片数据的方式同理,不过代码比较多,就不一一展示了,感兴趣可以下载全量代码看。
要想将数据更新到服务卡片,得先了解服务卡片的运作机制。如果是通过IDE>>File>>New>>Service Widget添加的服务卡片,那么在MainAbility中会添加卡片的生命周期回调方法,参考下面的代码。
public class MainAbility extends Ability {
... ...
protected ProviderFormInfo onCreateForm(Intent intent) {...}//在服务卡片上右击>>服务卡片(或上滑)时,通知接口
protected void onUpdateForm(long formId) {...}//在服务卡片请求更新,定时更新时,通知接口
protected void onDeleteForm(long formId) {..}//在服务卡片被删除时,通知接口
protected void onTriggerFormEvent(long formId, String message) {...}//JS服务卡片click时,通知接口
}
按照上述分析,我们只需要在config.json中开启服务卡片的周期性更新,在onUpdateForm(long formId)方法下执行数据获取更新。
config.json文件“abilities”的forms模块配置细节如下
"forms": [
{
"jsComponentName": "widget2",
"isDefault": true,
"scheduledUpdateTime": "10:30",//定点刷新的时刻,采用24小时制,精确到分钟。"updateDuration": 0时,才会生效。
"defaultDimension": "1*2",
"name": "widget2",
"description": "This is a service widget",
"colorMode": "auto",
"type": "JS",
"supportDimensions": [
"1*2"
],
"updateEnabled": true, //表示卡片是否支持周期性刷新
"updateDuration": 1 //卡片定时刷新的更新周期,1为30分钟,2为60分钟,N为30*N分钟
}
]
这样结合我们在上一步获取API数据,解析JSON,开启服务卡片的周期性更新,就可以在updateFormData()实现服务卡片的数据更新了。截取follower数据更新的部分代码如下
public void updateFormData(long formId, Object... vars) {
HiLog.info(TAG, "update form data: formId" + formId);
//这部分用来获取粉丝数
String url="https://api.bilibili.com/x/relation/stat?vmid=383565952";
ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {HiLog.info(TAG, "API返回失败");}
@Override
public void onResponse(String s) {
HiLog.info(TAG, "API返回成功");
try {
//1.调用fastjson解析,结果保存在JSON对应的类
BilibiliFollower bilibiliFollower=JSON.parseObject(s,BilibiliFollower.class);
//2.get方法获取解析内容
BilibiliFollower.Data data=bilibiliFollower.getData();
System.out.println("解析成功"+data.getFollower());
//这部分用来更新卡片信息
ZSONObject zsonObject=new ZSONObject(); //1.将要刷新的数据存放在一个ZSONObject实例中
zsonObject.put("follower",data.getFollower()); //2.更新数据,data.getFollower()就是在API数据请求中获取的粉丝数。
FormBindingData formBindingData=new FormBindingData(zsonObject); //3.将其封装在一个FormBindingData的实例中
try {
((MainAbility)context).updateForm(formId,formBindingData); //4.调用MainAbility的方法updateForm(),并将formBindingData作为第二个实参
} catch (FormException e) {
e.printStackTrace();
HiLog.info(TAG, "更新卡片失败");
}
} catch (Exception e) {
HiLog.info(TAG, "解析失败");
}
}
});
}
正常来说这样就可以正常更新数据了,但是会有个问题。就是在服务卡片首次创建添加到桌面的时候,在添加完的至少30分钟里,数据是不会更新的。此时如果在index.json中设置初始信息,那么在添加完成的前30分钟数据都是写死在data中的。如果不设置初始信息那么卡片就是空白的。
所以按照前面服务卡片的运作机制的分析,我们还需要在卡片初始化onCreateForm()的时候进行一次更新。这个非常简单用onCreateForm()调用onUpdateForm(formId)即可。
@Override
protected ProviderFormInfo onCreateForm(Intent intent) {
... ...
//初始化时先在线更新一下卡片
onUpdateForm(formId);
return formController.bindFormData();
}
总结:这里的onUpdateForm(formId)中API的网络请求一定要新开一个子线程,不然会影响页面加载。这也是前面说的用ZZRhttp的原因。不过现在也遇到一个问题,当卡片数量变多时,同时在线更新这么多的卡片会变得非常缓慢,这个问题还有待解决。
目前服务卡片仅支持click通用事件,事件类型:跳转事件(router)和消息事件(message)。详细说明参考官方文档
接下来实现与服务卡片的交互,当点击服务卡片时,会跳转到相应的页面,所以这里使用跳转事件。以番剧更新的卡片为例
1.首先我们要先添加一个要跳转的页面。如下图所示添加一个Page Ability,比如:VideoSlice
2.新建完成之后会增加VideoSlice和 slice/VideoSliceSlice 两个文件,和base/layout/ability_bilibili_page.xml页面文件
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_video);
Text text=(Text) findComponentById(ResourceTable.Id_text);
text.setText("页面跳转中");
// 随机图片数组
int[] resource={ResourceTable.Media_36e,ResourceTable.Media_36g,ResourceTable.Media_36h,ResourceTable.Media_38p};
Component component=findComponentById(ResourceTable.Id_image);
if (component instanceof Image) {
Image image=(Image) component;
image.setPixelMap(resource[(int)(Math.random()*3)]);//随机显示一张图片
}
String url="https://m.bilibili.com";
String param=intent.getStringParam("params");//从intent中获取 跳转事件定义的params字段的值
if(param !=null){
ZSONObject data=ZSONObject.stringToZSON(param);
url=data.getString("url");
}
webview(url);
}
//启动webview
public void webview(String url){
WebView webView=(WebView) findComponentById(ResourceTable.Id_webview);
webView.getWebConfig().setJavaScriptPermit(true); // 如果网页需要使用JavaScript,增加此行;如何使用JavaScript下文有详细介绍
webView.load(url);
}
3.增加webview,将页面默认的Text控件修改为webview
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<ohos.agp.components.webengine.WebView
ohos:id="$+id:webview"
ohos:height="match_parent"
ohos:width="match_parent">
</ohos.agp.components.webengine.WebView>
</DirectionalLayout>
4.在index.hml中给要触发的控件上添加onclick,比如:onclick=“routerEvent1”
<div class="div_root" ><!--在服务卡片设置一个 根div 横向布局-->
<div class="div_container"><!--在根div 横向放置4个div,每个div内部从上往下排列-->
<image class="item_image" src="{{ src1 }}" onclick="routerEvent1"></image>
<text class="item_title">{{ itemTitle1 }}</text>
<text class="item_content">{{ itemContent1 }}</text>
</div>
... ...
</div>
5.在index.json中,添加对应的actions,跳转事件要多加一个参数"abilityName",指定要跳转的页面,并且携带参数url。
{
"data": {
},
"actions": {
"routerEvent1": {
"action": "router",
"bundleName": "com.liangzili.servicewidget",
"abilityName": "com.liangzili.servicewidget.VideoSlice",
"params": {
"url": "{{url1}}"
}
},
"routerEvent2": {
... ...
}
这里使用视频动态服务卡片,做一个消息事件的测试,效果如下图,点击左右边,实现服务卡片的滑动。在小卡片上这样的操作体验不好。所以消息事件中的例子,只是为了测试,并没有加到项目里。
1.在index.hml中给要触发的控件上添加onclick,比如:onclick=“sendMessageEvent”
<-- 为了方便测试,直接将onclick添加在左右两侧的div组件上 -->
<div class="div" onclick="sendMessageEvent0">
<image class="item_image" src="{{ src0 }}"></image>
<text class="item_title">{{ itemTitle0 }}</text>
<text class="item_content">{{ itemContent0 }}</text>
</div>
<div class="div" onclick="sendMessageEvent1">
<image class="item_image" src="{{ src1 }}"></image>
<text class="item_title">{{ itemTitle1 }}</text>
<text class="item_content">{{ itemContent1 }}</text>
</div>
2.在index.json中,添加对应的actions
{
"data": {
},
"actions": {
"sendMessageEvent0": {
"action": "message",
"params": {
"p1": "left",
"index": "{{index}}"
}
},
"sendMessageEvent1": {
"action": "message",
"params": {
"p1": "right",
"index": "{{index}}"
}
}
}
}
3.如果是消息事件(message)当点击带有onclick的控件时,会触发MainAbility下的这个函数
@Override
protected void onTriggerFormEvent(long formId, String message) {
HiLog.info(TAG, "onTriggerFormEvent: " + message); //params的内容就通过message传递过来
super.onTriggerFormEvent(formId, message);
FormControllerManager formControllerManager=FormControllerManager.getInstance(this);
FormController formController=formControllerManager.getController(formId);//通过formId得到卡片控制器
formController.onTriggerFormEvent(formId, message);//接着再调用,对应的控制器 WidgetImpl
}
4.最后调用卡片控制器 WidgetImpl 中的onTriggerFormEvent()
public void onTriggerFormEvent(long formId, String message) {
HiLog.info(TAG, "onTriggerFormEvent."+message);
//先获取message中的参数
ZSONObject data=ZSONObject.stringToZSON(message);
String p1=data.getString("p1");
Integer index=data.getIntValue("index");
ZSONObject zsonObject=new ZSONObject(); //将要刷新的数据存放在一个ZSONObject实例中
Integer indexMax=2; //有N个滑块组件就设置N-1
if(p1.equals("right")){ //判断点击方向,如果是右侧
if(index==indexMax){index=-1;} //实现循环滚动
index=index+1;
zsonObject.put("index",index);
}else { //判断点击方向,如果是左侧
if(index==0){index=indexMax+1;} //实现循环滚动
index=index-1;
zsonObject.put("index",index);
}
FormBindingData formBindingData=new FormBindingData(zsonObject);
try {
((MainAbility)context).updateForm(formId,formBindingData);
} catch (FormException e) {
e.printStackTrace();
HiLog.info(TAG, "更新卡片失败");
}
}
list组件只能添加一个onclick,而且在点击的同时还需要获取点击的是list列表中的哪一项,这个比较特殊。
<list class="list" else>
<list-item for="{{list}}" class="list-item">
<div class="div" onclick="sendRouteEvent">
... ...
</div>
</list-item>
</list>
这个坑折磨了我好久,最终我发现在index.json中,可以使用item,item,idx获取到hml页面list的元素变量和索引。但是在官方文档并没有找到相关的内容,尝试了很久才解决这个问题。之后的部分就和跳转事件一样了,使用Video页面解析url进行播放就可以了。
"actions": {
"sendRouteEvent": {
"action": "router",
"bundleName": "com.liangzili.demos",
"abilityName": "com.liangzili.demos.Video",
"params": {
"url": "{{$item.short_url}}",
"index": "{{$idx}}"
}
}
}
总结:解决了list的点击事件之后,才发现这歌控件真是好用。能用list还是list方便。
启动之后的页面主要是为了登录账号,因为大部分的API是需要登录之后才可以获取到的。
在base/layout/ability_main.xml中添加webview组件,代码如下
<ohos.agp.components.webengine.WebView
ohos:id="$+id:webview"
ohos:height="match_parent"
ohos:width="match_parent">
</ohos.agp.components.webengine.WebView>
然后在启动页面执行加载操作。但其实加载前需要先从数据库中提取cookie信息,这个接下来说。
String url="https://m.bilibili.com";
WebView webView=(WebView) findComponentById(ResourceTable.Id_webview);
webView.getWebConfig().setJavaScriptPermit(true); // 如果网页需要使用JavaScript,增加此行;如何使用JavaScript下文有详细介绍
webView.load(url);
com/liangzili/demos/utils/CookieUtils.java
public class CookieUtils {
private static final HiLogLabel TAG=new HiLogLabel(HiLog.DEBUG,0x0,CookieUtils.class.getName());
/**
* 使用关系型数据库[读取]Cookie
* @param preferences
* @param url
*/
public static void ExtarctCookie(Preferences preferences, String url){
Map<String, ?> map=new HashMap<>();
//先从数据库中取出cookie
map=PreferenceDataBase.GetCookieMap(preferences);
//然后写入到cookieStore
CookieStore cookieStore=CookieStore.getInstance();//1.获取一个CookieStore的示例
for (Map.Entry<String, ?> entry : map.entrySet()) {
HiLog.info(TAG,entry.getKey()+"="+entry.getValue().toString());
cookieStore.setCookie(url,entry.getKey()+"="+entry.getValue().toString());//2.写入数据,只能一条一条写
}
}
/**
* 使用关系型数据库[保存]Cookie
* @param preferences 数据库的Preferences实例
* @param url 指定Cookie对应的域名
*/
public static void SaveCookie(Preferences preferences,String url){
//先取出要保存的cookie
CookieStore cookieStore=CookieStore.getInstance();
String cookieStr=cookieStore.getCookie(url);
HiLog.info(TAG,"saveCookie(String url)"+url+cookieStr);
//然后将cooke转成map
Map<String,String> cookieMap=cookieToMap(cookieStr);
//最后将map写入数据库
PreferenceDataBase.SaveMap(preferences,cookieMap);
}
// cookieToMap
public static Map<String,String> cookieToMap(String value) {
Map<String, String> map=new HashMap<String, String>();
value=value.replace(" ", "");
if (value.contains(";")) {
String values[]=value.split(";");
for (String val : values) {
String vals[]=val.split("=");
map.put(vals[0], vals[1]);
}
} else {
String values[]=value.split("=");
map.put(values[0], values[1]);
}
return map;
}
}
数据库的操作主要是com/liangzili/demos/database/PreferenceDataBase.java 这个类。使用轻量级偏好型数据库,更符合我们这里的需求。
public class PreferenceDataBase {
private static final HiLogLabel TAG=new HiLogLabel(HiLog.DEBUG,0x0,PreferenceDataBase.class.getName());
/**
* 获取Preferences实例
* @param context 数据库文件将存储在由context上下文指定的目录里。
* @param name fileName表示文件名,其取值不能为空,也不能包含路径
* @return //返回对应数据库的Preferences实例
*/
public static Preferences register(Context context,String name) {
DatabaseHelper databaseHelper=new DatabaseHelper(context);
Preferences preferences=databaseHelper.getPreferences(name);
return preferences;
}
... ...
}
2.从数据库中保存和读取Map
/**
* Map[保存]到偏好型数据库
* @param preferences 数据库的Preferences实例
* @param map 要保存的map
*/
public static void SaveMap(Preferences preferences,Map<String,String> map){
// 遍历map
for (Map.Entry<String, String> entry : map.entrySet()) {
HiLog.info(TAG,entry.getKey() + "=" + entry.getValue());
preferences.putString(entry.getKey(),entry.getValue());//3.将数据写入Preferences实例,
}
preferences.flushSync();//4.通过flush()或者flushSync()将Preferences实例持久化。
}
/**
* 从偏好型数据库[读取]Map
* @param preferences 数据库的Preferences实例
* @return 要读取的map
*/
public static Map<String,?> GetCookieMap(Preferences preferences){
Map<String, ?> map=new HashMap<>();
map=preferences.getAll();//3.读取数据
return map;
}
3.提取某些Cookie的值
/**
* 获取Cookie中的SESSDATA值
* @param context 上下文用来指定数据文件存储路径
* @return Cookie中的SESSDATA值
*/
public static String getSessData(Context context){
// 开启数据库
DatabaseHelper databaseHelper=new DatabaseHelper(context);//1.创建数据库使用数据库操作的辅助类
Preferences preferences=databaseHelper.getPreferences("bilibili");//2.获取到对应文件名的Preferences实例,filename是String类型
String SESSDATA=preferences.getString("SESSDATA",""); //3.读取数据
return SESSDATA;
}
/**
* 获取Cookie中的Vmid值
* @param context
* @return Cookie中的Vmid值
*/
public static String getVmid(Context context){
// 开启数据库
DatabaseHelper databaseHelper=new DatabaseHelper(context);//1.创建数据库使用数据库操作的辅助类
Preferences preferences=databaseHelper.getPreferences("bilibili");//2.获取到对应文件名的Preferences实例,filename是String类型
String DedeUserID=preferences.getString("DedeUserID",""); //3.读取数据
return DedeUserID;
}
——————
原创:老王丨鸿蒙hms开发者高级认证持证人!学习更多鸿蒙OS相关开发技术可以关注我的公众号:鸿蒙开发者老王
论文最苦恼的事情是什么?
到处找资料、到处找数据!
到哪找文献资料?这个问题我已经在之前帮你们解决啦!
详情看这篇文章:
大学资料、文献查找、毕业论文?还在苦恼吗?这几个网站足够使用
那到哪里去找统计数据呢?又该怎样查重呢?
要知道写论文嘛,动不动就得来一句:
根据调查,中国2020年......相比往年......
那今天,我们今天就来推荐几个有着超全统计数据的网站!并且介绍些写论文所需要的工具。
大数据导航
网址:http://hao.199it.com/
这个网站太厉害了,我要把它单独放一类!
这个网站之所以厉害,是因为它集合了近千个数据网站!包括大量统计国外、全球数据的网站!且对于每一类数据网站做了详细分类,目前已有113个分类(如果我没数错的话),每个分类,少则四五个网站,多则上十个!
比如,我找到了一个实时网络攻击地图
虽然我也不知道它是用来干什么的......
但是,有了这个导航网站,还怕找不到数据吗?
同时,网站主页还提供了几十个热门工具网站链接。
往下滑,可以搜索你想要的数据网站。
是不是很厉害?!!!
而且我还发现,网站贴心的对付费网站做了标注enna~
1、政府开放数据平台
1、国家数据
网址:http://data.stats.gov.cn/
由中国国家统计局所提供的数据,包含了我国经济民生教育等多个方面的资料,并且范围覆盖月、季、年三个方面,其它还有普查数据、地区数据、国际数据等,简直不要太好用!
其它可能用到的:
1)国家知识产权局
网址:http://www.sipo.gov.cn
(提供相关专利查询)
2)中国海关
网址:http://www.customs.gov.cn
(中国进出口的相关数据)
3)中国人民银行
网址:http://www.pbc.gov.cn/
(关于中国金融市场的相关政策及数据)
4)银监会
网址:http://www.cbrc.gov.cn
(关于银行金融等相关数据)
5)中国证监会
网址http://www.csrc.gov.cn
(查找中国证券的相关信息)
6)工业和信息化部
网址:http://www.miit.gov.cn
(大量的有关中国工业及信息化数据)
再提供一个地区性政府间经济合作组织的网站,就是大名鼎鼎的:
7)经合组织(OECD)
网址:https://data.oecd.org
(里面有很多成员国相关的统计数据)
还记得文章开头提到的那个导航网站吗?
它里面提供了很多国外政府开放数据平台,具体见下图:(链接就不贴了,直接去文章最上面的那个网站找)
2、国家数据及外国统计数据
1)国家数据
网址:http://data.stats.gov.cn/
(这个刚刚介绍了,为了保持本部分内容完整性,把它拿出来再说一遍。)
2)教育部
网址:http://www.moe.gov.cn/jyb_sjzl/
如果你想了解更多外国或国际教育数据,还是一样,去文章最开始推荐的那个大数据导航网站找,贴个图:
3)农业部
网址:http://zdscxx.moa.gov.cn/
4)生态环境部
网址:http://www.mee.gov.cn/hjzl/
5)人力资源与社会保障部
网址:http://www.mohrss.gov.cn/
6)卫生健康委员会
网址:http://www.nhc.gov.cn/wjw/xinx/xinxi.shtml
如果你想知道其它国家统计数据,比如什么加拿大、韩国之类的,同样,大数据导航网站里搜,贴个图:
保守估计有六十多个国家的统计网站吧
3、学术类网站数据
中国知网
网址:https://cnki.net/
万丈数据知识服务平台
网址:http://www.wanfangdata.com.cn/index.html
爱学术
网址:https://www.ixueshu.com/
这几个文献网站都提供专业的统计数据服务,相对来说,这些网站提供的数据使用频率更高些。拿知网举例,下图蓝框里的内容,就是知网提供的数据平台:(其它网站同理)
4、互联网趋势分析
看:
当然还是那个网站了( ̄▽ ̄)"
网址:http://hao.199it.com/
点击后直接跳转,方便吧!
1、问卷调查工具
这个我觉得,就用腾讯问卷啦,有微信小程序,问卷链接还可以直接转到qq里,不管是填问卷的人,还是收集数据的人,都挺方便的。
网址:https://wj.qq.com/
如果你想用其它网站......
篇幅原因,这里就不过多讲了,以后有机会抽时间做下讲解,直接看上图。
嗯,还是那个网站。。。
2、论文查重具体流程
论文查重,不管你使用哪种查重网站,最重要的是:
以学校要求的查重系统为准!学校用什么,你就用什么!
因为不同的平台,它的论文库和查重所使用的算法是不同的,所以用其它平台进行论文查重,可能并没有很大的参考价值!
具体流程:
1、在初稿完成后,去某宝购买一次查重服务。
注意:
(1)请选择合规范的第三方店铺。像知网,正规的商品订单都是可以验明真伪的,所以,如果订单无法验明真伪的话,不管别人再怎么说得天花乱坠,也不要相信!
(一般查重一次的最低价也需要二三十左右,太过便宜的一定要慎重选择,毕竟成本在那里)
(2)不要经过他人之手查重。因为现在第三方泄密的例子真的不少。比如,你在某宝购买了某平台的查重服务,一定不要让客服代替你去查重,同时,查重的文档也记得删掉个人学校等相关信息。
2、初稿查重完毕后,修改重复部分,再次购买查重服务,确保重复率在学校要求之下,同时预留一定的比例。(比如,学校要求百分之二十,那你就弄到百分之十五以下。
3、使用学校提供的查重服务,做最后的查重。(一般会提供一到两次机会)
关注人质综合,带你了解更多有趣又实用的网站app!
欢迎评论区留言哦!
核桃真的能补脑吗?(图片来源:Pixabay)
每天五分钟,你就能提高以下能力:
专注力、头脑敏捷性、语言能力、协调能力、问题解决能力和记忆力,这是真的吗?
芥末堆 洛克 2016年8月12日报道
某日,芥末堆编辑部的小梅正聚精会神地拿着手机点点划划,走近一看,原来他在玩手机游戏。
面对大家呲之以鼻的态度,“这可不是普通的手机游戏,”小梅仿佛被冤枉的说,“这可是由专业的神经科学家和教育专家团队所开发的大脑训练游戏。”
据了解,这是一款名为Peak的App,它曾受到苹果公司App Store的推荐,在教育类app中排名第一。
Penny同学告诉我,她一年前就下载了同类的脑力训练App。在她的手机里,现在还有Lumosity,Fit Brains、Memorado等脑力训练App。这类App其实大同小异,它们都提供多种类型的趣味游戏,宣传能提升你的记忆力、注意力、问题解决能力、敏捷度、语言能力、协调力以及情绪控制能力。
在国外,此类应用受到了用户的欢迎,也得到了资本市场的关注。Lumosity称其用户已达7000万,分布于全球182个国家。它于2012年完成了D轮3150万美元的融资,总融资额已达7000万美元。作为的后起之秀的Peak在2015年四月完成A轮融资,总融资额1000万美元。来自德国的Memorado在2015年3月就已经有350万用户,Fit Brains更是有1800万的下载量。
就职于搜狐视频的红红是Peak的忠实用户。在第一次使用Peak时,它会让你做一系列游戏任务,然后根据你的表现给你评分。接下来每天都会要求你完成若干游戏任务。红红玩这个游戏大约有一个月的时间,每天都会利用碎片时间,总计花一小时左右来完成当天的任务。对她来说,游戏本身十分有趣。而游戏成绩的不断上升也能给她带来成就感。这些游戏本来是可以将成绩分享到Facebook,不过本地化并没有做好,目前还不支持将游戏成绩分享到朋友圈和微博上。不过红红曾手动截屏然后分享到自己的朋友圈中。
小梅告诉我,对他而言,玩此类游戏的重要原因,便是社会比较所带来的优越感。经过一个多月,每天15分钟的练习,小梅的游戏成绩已经比游戏库里的同龄人要高出不少。他看着自己“大师级”的游戏评价,笑得十分满意。
小梅的白框已经比同龄人的蓝框要大了不少
出色的游戏设计和其所宣称的功效是此类App吸引用户最主要的两大因素。红红告诉我,她并没有完全相信此类游戏所宣称的提升脑力的效果。但她依然被其背后的神经科学光环所打动。Penny也是如此,一些人本来都会花点时间玩手机游戏,为什么不花时间玩这些同样有趣但是宣称能提升智力的教育游戏呢?每个人都希望变得更聪明,如果市场上真的出现某种能提升人类智力的食物、药物、课程、游戏等产品,那一定会大受欢迎。
App store中的脑力训练类app
在苹果的App store中,编辑们将一些应用聚合在一起,称之为“脑力训练”。此类App大多是基础版本免费,而要想玩更多的脑力训练游戏,就需要用户付费购买高级版本。小梅已经花了30元,买了一个月的高级版本。而红红则准备花数百元购买终身高级版本。
这种所谓的“脑力训练”游戏具体是什么样的呢?有的游戏会在屏幕上呈现数字,有正数也有负数,要求你从小到大点击这些数字。有的游戏会在屏幕上显示一些方块,然后这些方块会消失,要求玩家点击方块曾经的位置。
要求迅速从小到大点击数字的游戏
让我们以Lumosity中的“Color Match”游戏为例进行深度的介绍。该游戏会给你呈现不同颜色的文字。比如绿色的“绿色”,红色的“绿色”,黑色的“蓝色”,黄色的“黄色”等等。它要求你又快又好地判断字的颜色和含义是否相符。比如绿色的“绿色”便是相符,红色的“绿色”便是不相符。
有心理学相关背景的朋友应该马上就会明白,这是一种典型的认知心理学实验范式,叫做Stroop效应。当文字呈现在人类眼前时,对语义的信息加工和颜色的信息加工都会无意识地快速进行,而当两者不符时,便会产生相互干扰。
而这个“Color Match”游戏任务,会记录你在单位时间内答对的题目次数,然后给出相应的分数。随着你玩这个游戏的次数越来越多,你变得越来越熟练,取得的分数也越来越高。它宣称,你玩这个游戏玩得越好,思维灵活性就越高。因为你玩这个游戏的分数提高了,所以它宣称你的思维灵活性也得到了锻炼,也提高了。
表面看来,这话说得很有道理。但它其实只说对了一半。认知心理学家们的确会使用Stroop效应来测量人的认知抑制能力。因为Stroop效应中的成绩和一个人的认知抑制能力有一定程度的正相关。认知抑制能力是一个专业术语,大家可以简单地理解成,在别的条件不变的情况下,认知抑制能力越高,你就越聪明。
可以这么说,如果大家事先都没有刻意练习过这个Stroop效应的实验。如果你在第一次玩这个“Color Match”中所获得的分数如果比别人第一次玩要高,那么你的认知抑制能力就有较高可能性比别人高。但是,这不意味着大家都刻意地练习玩这个游戏,然后游戏分数提升之后,就能提升认知抑制能力。
用一个十分恰当的比方,智商测验可以用来估测一个人的聪明程度。大家都来参加智商测验,如果你的分数比别人高,那么你很可能就比别人聪明。但是这不意味着,大家不断地参加智商测验,做此类智商测验变得越来越熟练,获得越来越高的分数,然后就意味着自己的智商也变得越来越高了。
我们要区分两种效应,一种叫“练习效应”,一种叫“迁移效应”。粗略地说,前者是指你能通过不断地练习,来提升自己在某项测验中的成绩,而后者是指,你提升自己在某项测验中的成绩之后,在其他项目中的成绩也能提高。很多测验项目都具备练习效应,这很容易理解。但要具备迁移效应,就非常难。跑步是具备迁移效应的,你跑步成绩提升之后,游泳成绩甚至篮球成绩都有相应的提升。但是智商测验就没有迁移效应。
Lumosity自己的科学实验室发布研究报告,称Lumosity能有效的提升人类的认知能力。然而,Lumosity曾因为虚假宣传被美国联邦贸易委员会罚款两百万美元。因为在心理学界,科学家们的主流研究结果都认为,没有任何证据能说明此类脑力训练类app真的能提升人类的认知能力。因为找不到长期有效的实验室外迁移效应。
综合来看,你玩这类游戏分数越来越高,仅仅意味着你玩这类游戏分数越来越高,并不意味着你变得更聪明了,你的工作绩效不一定能上涨,你的考试成绩也不一定会上升。
1、本文是芥末堆网原创文章,转载此文章请注明出处(芥末堆网)及本页链接://www.jiemodui.com/N/60868.html。
2、芥末堆不接受通过公关费、车马费等任何形式发布失实文章,只呈现有价值的内容给读者。
3、如果你也从事教育,并希望被芥末堆报道,请发邮件到 tianyi.mei@jmdedu.com 告诉我们。
来源: 芥末堆
*请认真填写需求信息,我们会在24小时内与您取得联系。