private String refresh_token;
private long expires_in;
private String scope;
private String session_key;
private String access_token;
private String session_secret;
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public String getSession_key() {
return session_key;
}
public void setSession_key(String session_key) {
this.session_key = session_key;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getSession_secret() {
return session_secret;
}
public void setSession_secret(String session_secret) {
this.session_secret = session_secret;
}
}
这是通过刚才的返回数据生成的实体Bean,当网络请求返回数据后通过Retrofit会解析成这个返回实体。
下面添加接口,在network包下新建一个ApiService接口,里面的代码如下:
package com.llw.imagediscerndemo.network;
import com.llw.imagediscerndemo.model.GetTokenResponse;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Headers;
import retrofit2.http.POST;
/**
-
API服务
-
@author llw
-
@date 2021/4/1 17:48
*/
public interface ApiService {
/**
-
获取鉴权认证Token
-
@param grant_type 类型
-
@param client_id API Key
-
@param client_secret Secret Key
-
@return GetTokenResponse
*/
@FormUrlEncoded
@POST(“/oauth/2.0/token”)
Call getToken(@Field(“grant_type”) String grant_type,
@Field(“client_id”) String client_id,
@Field(“client_secret”) String client_secret);
}
这里还有一个接口呢,那就是图像识别接口。地址为:
https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general
找个接口比较特殊,官方文档的描述如下:
看你是否理解了。返回数据如下:
{
“log_id”: 327863200205075661,
“result_num”: 5,
“result”: [{
“score”: 0.967622,
“root”: “公众人物”,
“baike_info”: {
“baike_url”: “http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,
“image_url”: “http://imgsrc.baidu.com/baike/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg”,
“description”: “新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。”
},
“keyword”: “新垣结衣”
},
{
“score”: 0.716067,
“root”: “人物-人物特写”,
“keyword”: “头发”
},
{
“score”: 0.421281,
“root”: “商品-穿戴”,
“keyword”: “围巾”
},
{
“score”: 0.22347,
“root”: “商品-五金”,
“keyword”: “拉链”
},
{
“score”: 0.028031,
“root”: “商品-穿戴”,
“keyword”: “脖套”
}]
}
通过这个返回示例数据,可以生成一个实体Bean。在model包下新建一个GetDiscernResultResponse类,代码如下:
package com.llw.imagediscerndemo.model;
import java.util.List;
/**
- 获取识别结果响应实体
*/
public class GetDiscernResultResponse {
/**
-
log_id : 327863200205075661
-
result_num : 5
-
result : [{“score”:0.967622,“root”:“公众人物”,“baike_info”:{“baike_url”:“http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,“image_url”:“http://imgsrc.baidu.com/baike/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg”,“description”:“新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。”},“keyword”:“新垣结衣”},{“score”:0.716067,“root”:“人物-人物特写”,“keyword”:“头发”},{“score”:0.421281,“root”:“商品-穿戴”,“keyword”:“围巾”},{“score”:0.22347,“root”:“商品-五金”,“keyword”:“拉链”},{“score”:0.028031,“root”:“商品-穿戴”,“keyword”:“脖套”}]
*/
private long log_id;
private int result_num;
private List result;
public long getLog_id() {
return log_id;
}
public void setLog_id(long log_id) {
this.log_id = log_id;
}
public int getResult_num() {
return result_num;
}
public void setResult_num(int result_num) {
this.result_num = result_num;
}
public List getResult() {
return result;
}
public void setResult(List result) {
this.result = result;
}
public static class ResultBean {
/**
-
score : 0.967622
-
root : 公众人物
-
baike_info : {“baike_url”:“http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884”,“image_url”:“http://imgsrc.baidu.com/baike/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg”,“description”:“新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。”}
-
keyword : 新垣结衣
*/
private double score;
private String root;
private BaikeInfoBean baike_info;
private String keyword;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public BaikeInfoBean getBaike_info() {
return baike_info;
}
public void setBaike_info(BaikeInfoBean baike_info) {
this.baike_info = baike_info;
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public static class BaikeInfoBean {
/**
-
baike_url : http://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884
-
image_url : http://imgsrc.baidu.com/baike/pic/item/91ef76c6a7efce1b27893518a451f3deb58f6546.jpg
-
description : 新垣结衣(Aragaki Yui),1988年6月11日出生于冲绳县那霸市。日本女演员、歌手、模特。毕业于日出高中。2001年,参加《nicola》模特比赛并获得最优秀奖。2005年,因出演现代剧《涩谷15》而作为演员出道。2006年,参演校园剧《我的老大,我的英雄》;同年,她还出版了个人首本写真集《水漾青春》。2007年,她从日出高校毕业后开始专注于演艺发展,并发表个人首张音乐专辑《天空》;同年,新垣结衣还主演了爱情片《恋空》,而她也凭借该片获得了多个电影新人奖项。2010年,主演爱情片《花水木》。2011年,主演都市剧《全开女孩》。2012年,相继参演现代剧《Legal High》、剧情片《剧场版新参者:麒麟之翼》。2013年,主演都市剧《飞翔情报室》。2014年,她主演了剧情片《黎明的沙耶》。2016年,主演爱情喜剧《逃避虽可耻但有用》,并凭借该剧获得了多个电视剧女主角奖项。2017年,主演爱情片《恋爱回旋》,凭借该片获得第60届蓝丝带奖最佳女主角;同年11月,她还凭借医疗剧《Code Blue 3》获得第94届日剧学院赏最佳女配角。
*/
private String baike_url;
private String image_url;
private String description;
public String getBaike_url() {
return baike_url;
}
public void setBaike_url(String baike_url) {
this.baike_url = baike_url;
}
public String getImage_url() {
return image_url;
}
public void setImage_url(String image_url) {
this.image_url = image_url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
}
下面在ApiService中添加接口。
/**
-
获取图像识别结果
-
@param accessToken 获取鉴权认证Token
-
@param url 网络图片Url
-
@return JsonObject
*/
@FormUrlEncoded
@POST(“/rest/2.0/image-classify/v2/advanced_general”)
@Headers(“Content-Type:application/x-www-form-urlencoded; charset=utf-8”)
Call getDiscernResult(@Field(“access_token”) String accessToken,
@Field(“url”) String url);
我这个写法和官方说明好像有点不同,我没有用Body。选择直接把数据放在Url中请求。
现在万事具备了,下面先来获取鉴权认证Token。
打开MainActivity,添加如下代码:
private static final String TAG = “MainActivity”;
/**
- Api服务
*/
private ApiService service;
/**
- 鉴权Toeken
*/
private String accessToken;
然后在onCreate方法中对ApiService进行实例化。
service = ServiceGenerator.createService(ApiService.class);
新增如下方法获取Token,
/**
- 访问API获取接口
*/
private void requestApiGetToken() {
String grantType = “client_credentials”;
String apiKey = “TjPChftoEyBq7Nzm65KNerqr”;
String apiSecret = “eTph4jO95te6R3G2aecktGMbkieOv7rS”;
service.getToken(grantType, apiKey, apiSecret)
.enqueue(new NetCallBack() {
@Override
public void onSuccess(Call call, Response response) {
if (response.body() != null) {
//鉴权Token
accessToken = response.body().getAccess_token();
Log.d(TAG,accessToken);
}
}
@Override
public void onFailed(String errorStr) {
Log.e(TAG, “获取Token失败,失败原因:” + errorStr);
accessToken = null;
}
});
}
然后在onCreate中调用它。
运行一下,看一下控制台是否打印了日志。
你可以看到这个Token还是挺长的。对于这个Token,是有有效期的,基本上是一个月,所以你可以不用每次使用时都重新请求这个接口去获取Token,这里可以用缓存来解决这个问题。
说一下逻辑,当通过接口拿到Token时保存Token、Token获取时间、Token有效时长三个数据到缓存中,每一次使用前进行一次判断,首先是判断有没有Token,其次是判断Token有没有过期。那么按照这个思路我们就可以这么写代码了。
这里为了方便我在com.llw.imagediscerndemo包下新建一个util包,包下新建一个Constant类,里面的代码如下:
package com.llw.imagediscerndemo.util;
/**
- 全局常量
*/
public class Constant {
/**
- 鉴权Token
*/
public static final String TOKEN = “accessToken”;
/**
- 获取Token的时间
*/
public static final String GET_TOKEN_TIME = “getTokenTime”;
/**
- Token有效期
*/
public static final String TOKEN_VALID_PERIOD = “tokenValidPeriod”;
}
这三个值,我刚才也说明过了。下面写一个缓存的SPUtils工具类,里面的代码如下:
package com.llw.imagediscerndemo.util;
import android.content.Context;
import android.content.SharedPreferences;
/**
-
SharedPreferences工具类
-
@author llw
*/
public class SPUtils {
private static final String NAME = “config”;
public static void putBoolean(String key, boolean value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putBoolean(key, value).commit();
}
public static boolean getBoolean(String key, boolean defValue, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getBoolean(key, defValue);
}
public static void putString(String key, String value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putString(key, value).commit();
}
public static String getString(String key, String defValue, Context context) {
if (context != null) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getString(key, defValue);
}
return “”;
}
public static void putInt(String key, int value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putInt(key, value).commit();
}
public static int getInt(String key, int defValue, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getInt(key, defValue);
}
public static void putLong(String key, long value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putLong(key, value).commit();
}
public static long getLong(String key, long defValue, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getLong(key, defValue);
}
public static void remove(String key, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().remove(key).commit();
}
}
也是很简单的代码,相信你一眼就看明白了,下面就该在MainActivity中去处理缓存数据的存取了。
首先是放缓存,这当然是在请求接口的成功数据返回中放,修改onSuccess中的代码,如下。
@Override
public void onSuccess(Call call, Response response) {
if (response.body() != null) {
//鉴权Token
accessToken = response.body().getAccess_token();
//过期时间 秒
long expiresIn = response.body().getExpires_in();
//当前时间 秒
long currentTimeMillis = System.currentTimeMillis() / 1000;
//放入缓存
SPUtils.putString(Constant.TOKEN, accessToken, MainActivity.this);
SPUtils.putLong(Constant.GET_TOKEN_TIME, currentTimeMillis, MainActivity.this);
SPUtils.putLong(Constant.TOKEN_VALID_PERIOD, expiresIn, MainActivity.this);
}
}
然后写一个判断Token是否过期的方法,方法代码如下:
/**
-
Token是否过期
-
@return
*/
private boolean isTokenExpired() {
//获取Token的时间
long getTokenTime = SPUtils.getLong(Constant.GET_TOKEN_TIME, 0, this);
//获取Token的有效时间
long effectiveTime = SPUtils.getLong(Constant.TOKEN_VALID_PERIOD, 0, this);
//获取当前系统时间
long currentTime = System.currentTimeMillis() / 1000;
return (currentTime - getTokenTime) >= effectiveTime;
}
这个方法也是很好理解的,首先取出缓存中的获取Token的时间,然后获取Token的有效时长,再获取当前系统时间,然后通过当前系统时间减去获得Token的时间,得到的值再与Token有效期做比较,如果大于等于有效期则说明Token过期,返回true,否则返回false。
下面再写一个方法,用来获取Token,同时将我们之前写的代码给串起来。
/**
- 获取鉴权Token
*/
private String getAccessToken() {
String token = SPUtils.getString(Constant.TOKEN, null, this);
if (token == null) {
//访问API获取接口
requestApiGetToken();
} else {
//则判断Token是否过期
if (isTokenExpired()) {
//过期
requestApiGetToken();
} else {
accessToken = token;
}
}
return accessToken;
}
首先获取缓存中的Token,应用第一次进入肯定是没有值的,没有值则返回默认值null,那么token变量此时为null,那么就会通过接口去获取Token,当获取之后存入缓存,再次进入时,就不是null了,那么就会通过isTokenExpired()方法来判断Token是否过期,过期了也是通过网络请求重新拿到Token放入缓存,如果没有过期则直接使用缓存中的Token,最后返回Token。这方法在onCreate中调用
Token拿到以后我们来进行网络图片Url识别。先说一下思路,首先是通过网络图片url和Token去请求接口,然后获得返回值,此时要显示一个加载条,然后通过返回数据渲染列表,当数据显示在列表之后就完成了。
首先找一个网络图片Url,如下:
https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg
这个网络图片是一个水杯的图片,如下所示:
首先修改布局activity_main.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:gravity=“center”
android:orientation=“vertical”
tools:context=“.MainActivity”>
<ImageView
android:id=“@+id/iv_picture”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:scaleType=“centerCrop” />
<LinearLayout
android:layout_marginBottom=“16dp”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_alignParentBottom=“true”
android:gravity=“center”>
<Button
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:onClick=“IdentifyWebPictures”
android:text=“识别网络图片” />
<ProgressBar
android:visibility=“gone”
android:id=“@+id/pb_loading”
android:layout_centerInParent=“true”
android:layout_width=“60dp”
android:layout_height=“60dp”/>
然后在MainActivity中写入IdentifyWebPictures方法,代码如下:
/**
-
识别网络图片
-
@param view
*/
public void IdentifyWebPictures(View view) {
}
首先创建对象
/**
- 显示图片
*/
private ImageView ivPicture;
/**
- 进度条
*/
private ProgressBar pbLoading;
然后在onCreate中绑定xml中的控件id。
ivPicture = findViewById(R.id.iv_picture);
pbLoading = findViewById(R.id.pb_loading);
下面来修改IdentifyWebPictures()方法的代码,如下:
public void IdentifyWebPictures(View view) {
pbLoading.setVisibility(View.VISIBLE);
if (accessToken == null) {
showMsg(“获取AccessToken到null”);
return;
}
String imgUrl = “https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg”;
//显示图片
Glide.with(this).load(imgUrl).into(ivPicture);
showMsg(“图像识别中”);
service.getDiscernResult(accessToken, imgUrl).enqueue(new NetCallBack() {
@Override
public void onSuccess(Call call, Response response) {
List<GetDiscernResultResponse.ResultBean> result = response.body() != null ? response.body().getResult() : null;
if (result != null && result.size() > 0) {
//显示识别结果
showDiscernResult(result);
} else {
pbLoading.setVisibility(View.GONE);
showMsg(“未获得相应的识别结果”);
}
}
@Override
public void onFailed(String errorStr) {
pbLoading.setVisibility(View.GONE);
Log.e(TAG, “图像识别失败,失败原因:” + errorStr);
}
});
}
当点击按钮时,显示进度条,然后通过getAccessToken()方法获取Token,这里获取Token,有两种方式,通过网络获取和本地缓存获取,之后显示网络图片在ImageView控件中,Toast提示一下,之后请求的成功和失败的回调了,在成功的回调中先判断数据是否为空,不为空再通过showDiscernResult()方法去显示数据,下面写这个方法。
/**
-
显示识别的结果列表
-
@param result
*/
private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {
}
showMsg方法:
/**
-
Toast提示
-
@param msg 内容
*/
private void showMsg(String msg){
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
}
为了不占用屏幕的控件,我这里打算用一个弹窗来显示数据,弹窗里面是一个列表,列表通过item布局构建,数据由刚才的方法传递进来,我们一步一步来写,首先构建item的布局。在layout下新建一个item_result_rv.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:padding=“16dp”
android:background=“#FFF”
android:layout_marginBottom=“1dp”
android:layout_height=“wrap_content”>
<TextView
android:id=“@+id/tv_keyword”
android:textSize=“16sp”
android:textColor=“#000”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<TextView
android:layout_marginTop=“@dimen/dp_4”
android:layout_below=“@+id/tv_keyword”
android:id=“@+id/tv_root”
android:textSize=“14sp”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
<TextView
android:id=“@+id/tv_score”
android:layout_alignParentEnd=“true”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”/>
item布局有了,下面构建弹窗的布局代码,在layout下新建一个 dialog_bottom.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“#EEE”
android:orientation=“vertical”>
<TextView
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“#FFF”
android:gravity=“center”
android:padding=“16dp”
android:text=“识别结果” />
<androidx.recyclerview.widget.RecyclerView
android:id=“@+id/rv_result”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“1dp” />
下面布局都有了,先构建这个列表的适配器,在com.llw.imagediscerndemo下新建一个adapter包,包下新建一个DiscernResultAdapter类,里面的代码如下:
package com.llw.imagediscerndemo.adapter;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.imagediscerndemo.R;
import com.llw.imagediscerndemo.model.GetDiscernResultResponse;
import java.util.List;
/**
-
识别结果列表适配器
-
@author llw
*/
public class DiscernResultAdapter extends BaseQuickAdapter<GetDiscernResultResponse.ResultBean, BaseViewHolder> {
public DiscernResultAdapter(int layoutResId, @Nullable List<GetDiscernResultResponse.ResultBean> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, GetDiscernResultResponse.ResultBean item) {
helper.setText(R.id.tv_keyword,item.getKeyword())
.setText(R.id.tv_root,item.getRoot())
.setText(R.id.tv_score,String.valueOf(item.getScore()));
}
}
万事具备,只差显示数据了,下面进入MainActivity中,首先创建对象
/**
- 底部弹窗
*/
private BottomSheetDialog bottomSheetDialog;
/**
- 弹窗视图
*/
private View bottomView;
然后在onCreate中实例化,
bottomSheetDialog = new BottomSheetDialog(this);
bottomView = getLayoutInflater().inflate(R.layout.dialog_bottom, null);
然后修改showDiscernResult方法,代码如下:
private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {
bottomSheetDialog.setContentView(bottomView);
bottomSheetDialog.getWindow().findViewById(R.id.design_bottom_sheet).setBackgroundColor(Color.TRANSPARENT);
RecyclerView rvResult = bottomView.findViewById(R.id.rv_result);
DiscernResultAdapter adapter = new DiscernResultAdapter(R.layout.item_result_rv, result);
rvResult.setLayoutManager(new LinearLayoutManager(this));
rvResult.setAdapter(adapter);
//隐藏加载
pbLoading.setVisibility(View.GONE);
//显示弹窗
bottomSheetDialog.show();
}
下面运行一下:
可以看到结果识别到了。
在实际应用中,更多是采用本地的图片进行识别,通常是选择拍照的图片或者打开相册获取图片,先来看看通过相册获取图片进行图像识别。要实现这个功能首先要改一下接口,加一个image参数。
然后修改ImageDiscern方法。
/**
-
图像识别请求
-
@param token token
-
@param imageBase64 图片Base64
-
@param imgUrl 网络图片Url
*/
private void ImageDiscern(String token, String imageBase64, String imgUrl) {
service.getDiscernResult(token, imageBase64, imgUrl).enqueue(new NetCallBack() {
@Override
public void onSuccess(Call call, Response response) {
List<GetDiscernResultResponse.ResultBean> result = response.body() != null ? response.body().getResult() : null;
if (result != null && result.size() > 0) {
//显示识别结果
showDiscernResult(result);
} else {
pbLoading.setVisibility(View.GONE);
showMsg(“未获得相应的识别结果”);
}
}
@Override
public void onFailed(String errorStr) {
pbLoading.setVisibility(View.GONE);
Log.e(TAG, “图像识别失败,失败原因:” + errorStr);
}
});
}
这个方法接收三个参数,Token、ImageBase64、图片Url。ImageBase64和图片Url只能二选一。选其中一个另一个则传null。比如之前的通过网络图片Url识别。
接口的相关方法都改好了,下面来写打开相册的方法。Android6.0以后读写文件都属于危险权限,因此需要动态请求。在MainActivity中声明:
private RxPermissions rxPermissions;
然后在onCreate中实例化
rxPermissions = new RxPermissions(this);
下面修改布局,在之前的按钮后面再加一个按钮
<Button
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:onClick=“IdentifyAlbumPictures”
android:text=“识别相册图片” />
然后在MainActivity中增加IdentifyAlbumPictures方法,代码如下:
/**
-
识别相册图片
-
@param view
*/
@SuppressLint(“CheckResult”)
public void IdentifyAlbumPictures(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
rxPermissions.request(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(grant -> {
if (grant) {
//获得权限
openAlbum();
} else {
showMsg(“未获取到权限”);
}
});
} else {
openAlbum();
}
}
当获取到权限之后通过openAlbum()方法打开相册,openAlbum方法代码如下:
/**
- 打开相册
*/
private void openAlbum() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType(“image/*”);
startActivityForResult(intent, OPEN_ALBUM_CODE);
}
这里定义了一个请求码
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
private RxPermissions rxPermissions;
然后在onCreate中实例化
rxPermissions = new RxPermissions(this);
下面修改布局,在之前的按钮后面再加一个按钮
<Button
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:onClick=“IdentifyAlbumPictures”
android:text=“识别相册图片” />
然后在MainActivity中增加IdentifyAlbumPictures方法,代码如下:
/**
-
识别相册图片
-
@param view
*/
@SuppressLint(“CheckResult”)
public void IdentifyAlbumPictures(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
rxPermissions.request(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(grant -> {
if (grant) {
//获得权限
openAlbum();
} else {
showMsg(“未获取到权限”);
}
});
} else {
openAlbum();
}
}
当获取到权限之后通过openAlbum()方法打开相册,openAlbum方法代码如下:
/**
- 打开相册
*/
private void openAlbum() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType(“image/*”);
startActivityForResult(intent, OPEN_ALBUM_CODE);
}
这里定义了一个请求码
尾声
最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。
[外链图片转存中…(img-RcYfrEZH-1714325778914)]
Android进阶学习资料库
一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!
[外链图片转存中…(img-jR7YUAtp-1714325778915)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!