Android 垃圾分类APP(四)垃圾分类之图像输入(1)

android:layout_width=“match_parent”

android:layout_height=“@dimen/dp_60”

android:layout_margin=“@dimen/dp_16”

android:gravity=“center”

android:insetTop=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:onClick=“jumpImageInput”

android:text=“图像输入”

android:textSize=“@dimen/sp_16”

android:theme=“@style/Theme.MaterialComponents.Light.DarkActionBar”

app:backgroundTint=“@color/colorPrimaryDark”

app:cornerRadius=“@dimen/dp_12”

app:icon=“@mipmap/icon_image_input”

app:iconGravity=“textStart”

app:iconSize=“@dimen/dp_24” />

进入到MainActivity中,新增一个方法jumpImageInput。

/**

  • 进入图像输入页面

*/

public void jumpImageInput(View view) {

gotoActivity(ImageInputActivity.class);

}

下面来写ImageInputActivity页面的代码,写代码之前,先完成布局编写,修改activity_image_input,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:fitsSystemWindows=“true”

android:orientation=“vertical”>

<com.google.android.material.appbar.MaterialToolbar

android:id=“@+id/toolbar”

android:layout_width=“match_parent”

android:layout_height=“?attr/actionBarSize”

android:background=“@color/white”

android:elevation=“@dimen/dp_2”

app:navigationIcon=“@mipmap/icon_back”>

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_gravity=“center”

android:text=“图像输入”

android:textColor=“@color/black”

android:textSize=“18sp” />

</com.google.android.material.appbar.MaterialToolbar>

<androidx.core.widget.NestedScrollView

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:overScrollMode=“never”>

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:gravity=“center_horizontal”

android:orientation=“vertical”>

<ImageView

android:id=“@+id/iv_picture”

android:layout_width=“@dimen/dp_200”

android:layout_height=“@dimen/dp_300”

android:layout_marginTop=“@dimen/dp_12”

android:visibility=“gone” />

<EditText

android:id=“@+id/et_image_url”

android:layout_width=“match_parent”

android:layout_height=“@dimen/dp_50”

android:background=“@drawable/shape_et_bg”

android:hint=“网络图片Url”

android:layout_margin=“@dimen/dp_1”

android:textCursorDrawable=“@drawable/cursor_style”

android:paddingStart=“@dimen/dp_12”

android:paddingEnd=“@dimen/dp_12”

android:singleLine=“true”

android:imeOptions=“actionGo”

android:textSize=“@dimen/sp_14”

android:visibility=“gone” />

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:orientation=“horizontal”

android:padding=“@dimen/dp_12”>

<com.google.android.material.button.MaterialButton

android:id=“@+id/btn_web_picture”

style=“@style/Widget.MaterialComponents.Button.UnelevatedButton”

android:layout_width=“0dp”

android:layout_height=“@dimen/dp_50”

android:layout_marginEnd=“@dimen/dp_6”

android:layout_weight=“1”

android:gravity=“center”

android:insetTop=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:text=“网络图片”

android:textSize=“@dimen/sp_16”

android:theme=“@style/Theme.MaterialComponents.Light.DarkActionBar”

app:backgroundTint=“@color/colorPrimaryDark”

app:cornerRadius=“@dimen/dp_12”

app:iconGravity=“textStart”

app:iconSize=“@dimen/dp_24” />

<com.google.android.material.button.MaterialButton

android:id=“@+id/btn_open_album”

style=“@style/Widget.MaterialComponents.Button.UnelevatedButton”

android:layout_width=“0dp”

android:layout_height=“@dimen/dp_50”

android:layout_marginStart=“@dimen/dp_6”

android:layout_marginEnd=“@dimen/dp_6”

android:layout_weight=“1”

android:gravity=“center”

android:insetTop=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:text=“相册图片”

android:textSize=“@dimen/sp_16”

android:theme=“@style/Theme.MaterialComponents.Light.DarkActionBar”

app:backgroundTint=“@color/colorPrimaryDark”

app:cornerRadius=“@dimen/dp_12”

app:iconGravity=“textStart”

app:iconSize=“@dimen/dp_24” />

<com.google.android.material.button.MaterialButton

android:id=“@+id/btn_take_photo”

style=“@style/Widget.MaterialComponents.Button.UnelevatedButton”

android:layout_width=“0dp”

android:layout_height=“@dimen/dp_50”

android:layout_marginStart=“@dimen/dp_6”

android:layout_weight=“1”

android:gravity=“center”

android:insetTop=“@dimen/dp_0”

android:insetBottom=“@dimen/dp_0”

android:text=“拍照图片”

android:textSize=“@dimen/sp_16”

android:theme=“@style/Theme.MaterialComponents.Light.DarkActionBar”

app:backgroundTint=“@color/colorPrimaryDark”

app:cornerRadius=“@dimen/dp_12”

app:iconGravity=“textStart”

app:iconSize=“@dimen/dp_24” />

<LinearLayout

android:id=“@+id/lay_recognition_result”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:gravity=“center_horizontal”

android:orientation=“vertical”

android:visibility=“gone”>

<LinearLayout

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:gravity=“center_vertical”>

<View

android:layout_width=“@dimen/dp_30”

android:layout_height=“@dimen/dp_1”

android:background=“@color/line_color” />

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:padding=“@dimen/dp_12”

android:text=“识别结果” />

<View

android:layout_width=“@dimen/dp_30”

android:layout_height=“@dimen/dp_1”

android:background=“@color/line_color” />

<androidx.recyclerview.widget.RecyclerView

android:id=“@+id/rv_recognition_result”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:overScrollMode=“never” />

<LinearLayout

android:id=“@+id/lay_classification_result”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:gravity=“center_horizontal”

android:orientation=“vertical”

android:visibility=“gone”>

<LinearLayout

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:gravity=“center_vertical”>

<View

android:layout_width=“@dimen/dp_30”

android:layout_height=“@dimen/dp_1”

android:background=“@color/line_color” />

<TextView

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:padding=“@dimen/dp_12”

android:text=“分类结果” />

<View

android:layout_width=“@dimen/dp_30”

android:layout_height=“@dimen/dp_1”

android:background=“@color/line_color” />

<androidx.recyclerview.widget.RecyclerView

android:id=“@+id/rv_classification_result”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:overScrollMode=“never” />

</androidx.core.widget.NestedScrollView>

然后回到ImageInputActivity页面,再写代码之前,先想一下这个页面要做什么?首先是获取百度的鉴权Token,然后进行图片识别,最后进行物品的垃圾分类。那么就需要三次网络请求,这里需要重写一个订阅。

三、网络订阅


这里需要增加一个网络访问地址,因为使用的是百度的API,而本身有一个天行的API地址,这里需要对两个地址进行一个控制。打开NetworkApi,在里面增加如下方法。

/**

  • 修改访问地址

  • @param type

*/

private static void getBaseUrl(int type) {

switch (type) {

case 0://天行API地址

mBaseUrl = “http://api.tianapi.com”;

break;

case 1://百度SDK地址

mBaseUrl = “https://aip.baidubce.com”;

break;

default:

break;

}

}

这个方法根据传入类型的不同,使用不同的网络地址,之前写博客时疏忽了,写的嗨了,漏掉了这一部分,现在补上。然后在createService方法中增加一个type参数,之后调用getBaseUrl方法获取访问地址。

在这里插入图片描述

现在你的这个createService方法改动了,那么其他调用了这个方法的地方也要做相应的改动,比如之前在做文字输入进行垃圾分类识别时,TextContract中的调用,之前是没有type的,现在你加一个0就可以了,0表示就是访问天行API。

在这里插入图片描述

其他的地方记也要修改,否则会报错的。改好之后,就可以来写这个图像识别到的订阅器了,如下:

在contract包下新建一个ImageContract类,里面的代码如下:

package com.llw.goodtrash.contract;

import android.annotation.SuppressLint;

import com.llw.goodtrash.api.ApiService;

import com.llw.goodtrash.model.GetDiscernResultResponse;

import com.llw.goodtrash.model.GetTokenResponse;

import com.llw.goodtrash.model.TrashResponse;

import com.llw.mvplibrary.base.BasePresenter;

import com.llw.mvplibrary.base.BaseView;

import com.llw.mvplibrary.network.NetworkApi;

import com.llw.mvplibrary.network.observer.BaseObserver;

import static com.llw.goodtrash.utils.Constant.*;

/**

  • 图像输入页面访问网络

  • @author llw

  • @date 2021/3/30 15:28

*/

public class ImageContract {

public static class ImagePresenter extends BasePresenter {

/**

  • 获取鉴权Token

*/

@SuppressLint(“CheckResult”)

public void getToken() {

ApiService service = NetworkApi.createService(ApiService.class, 1);

service.getToken(GRANT_TYPE, API_KEY, API_SECRET)

.compose(NetworkApi.applySchedulers(new BaseObserver() {

@Override

public void onSuccess(GetTokenResponse getTokenResponse) {

if (getView() != null) {

getView().getTokenResponse(getTokenResponse);

}

}

@Override

public void onFailure(Throwable e) {

if (getView() != null) {

getView().getTokenFailed(e);

}

}

}));

}

/**

  • 获取图像识别结果

  • @param token 鉴权Token

  • @param image 图片base64

  • @param url 网络图片url

*/

@SuppressLint(“CheckResult”)

public void getDiscernResult(String token, String image, String url) {

ApiService service = NetworkApi.createService(ApiService.class, 1);

service.getDiscernResult(token, image, url)

.compose(NetworkApi.applySchedulers(new BaseObserver() {

@Override

public void onSuccess(GetDiscernResultResponse getTokenResponse) {

if (getView() != null) {

getView().getDiscernResultResponse(getTokenResponse);

}

}

@Override

public void onFailure(Throwable e) {

if (getView() != null) {

getView().getDiscernResultFailed(e);

}

}

}));

}

/**

  • 搜索物品

  • @param word 物品名

*/

@SuppressLint(“CheckResult”)

public void searchGoods(String word) {

ApiService service = NetworkApi.createService(ApiService.class, 0);

service.searchGoods(word).compose(NetworkApi.applySchedulers(new BaseObserver() {

@Override

public void onSuccess(TrashResponse groupResponse) {

if (getView() != null) {

getView().getSearchResponse(groupResponse);

}

}

@Override

public void onFailure(Throwable e) {

if (getView() != null) {

getView().getSearchResponseFailed(e);

}

}

}));

}

}

public interface ImageView extends BaseView {

/**

  • 获取鉴权Token

  • @param response GetTokenResponse

*/

void getTokenResponse(GetTokenResponse response);

/**

  • 获取鉴权Token异常返回

  • @param throwable 异常

*/

void getTokenFailed(Throwable throwable);

/**

  • 获取图像识别结果

  • @param response GetDiscernResultResponse

*/

void getDiscernResultResponse(GetDiscernResultResponse response);

/**

  • 获取图像识别结果失败

  • @param throwable 异常

*/

void getDiscernResultFailed(Throwable throwable);

/**

  • 搜索物品返回

  • @param response TrashResponse

*/

void getSearchResponse(TrashResponse response);

/**

  • 搜索物品异常返回

  • @param throwable 异常

*/

void getSearchResponseFailed(Throwable throwable);

}

}

鉴权方法中的几个全局变量在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”;

/**

  • 百度鉴权认证参数值

*/

public static final String GRANT_TYPE = “client_credentials”;

/**

  • 百度图像识别 APP ID GoodTrash

*/

public static final String APP_ID = “23943795”;

/**

  • 百度图像识别 APP Key GoodTrash

*/

public static final String API_KEY = “PAUCX7vSAd4ZBwv897GAfhEQ”;

请注意,这里的值是我在百度开放平台上注册应用时生成的,请替换为自己的。

下面回到ImageInputActivity,修改代码后如下:

package com.llw.goodtrash.ui;

import android.os.Bundle;

import com.llw.goodtrash.R;

import com.llw.goodtrash.contract.ImageContract;

import com.llw.goodtrash.model.GetDiscernResultResponse;

import com.llw.goodtrash.model.GetTokenResponse;

import com.llw.goodtrash.model.TrashResponse;

import com.llw.mvplibrary.mvp.MvpActivity;

/**

  • 图像输入物品进行垃圾分类

  • @author llw

  • @date 2021/4/7 11:04

*/

public class ImageInputActivity extends MvpActivity<ImageContract.ImagePresenter> implements ImageContract.ImageView {

@Override

public void initData(Bundle savedInstanceState) {

}

@Override

public int getLayoutId() {

return R.layout.activity_image_input;

}

@Override

protected ImageContract.ImagePresenter createPresenter() {

return new ImageContract.ImagePresenter();

}

@Override

public void getTokenResponse(GetTokenResponse response) {

}

@Override

public void getTokenFailed(Throwable throwable) {

}

@Override

public void getDiscernResultResponse(GetDiscernResultResponse response) {

}

@Override

public void getDiscernResultFailed(Throwable throwable) {

}

@Override

public void getSearchResponse(TrashResponse response) {

}

@Override

public void getSearchResponseFailed(Throwable throwable) {

}

}

这里使用了MVP,通过P来处理M和V,三个网络请求对应六个返回,下面进行页面的初始化

四、编写页面代码


先声明一些变量

private static final String TAG = “ImageInputActivity”;

/**

  • 打开相册

*/

private static final int OPEN_ALBUM_CODE = 100;

/**

  • 打开相机

*/

private static final int TAKE_PHOTO_CODE = 101;

/**

  • 鉴权Toeken

*/

private String accessToken;

private Toolbar toolbar;

private ImageView ivPicture;

private EditText etImageUrl;

private LinearLayout layRecognitionResult,layClassificationResult;

private RecyclerView rvRecognitionResult,rvClassificationResult;

private RxPermissions rxPermissions;

private File outputImage;

然后新增一个initView的方法。

/**

  • 初始化

*/

private void initView() {

toolbar = findViewById(R.id.toolbar);

ivPicture = findViewById(R.id.iv_picture);

etImageUrl = findViewById(R.id.et_image_url);

findViewById(R.id.btn_web_picture).setOnClickListener(this);

findViewById(R.id.btn_open_album).setOnClickListener(this);

findViewById(R.id.btn_take_photo).setOnClickListener(this);

layRecognitionResult = findViewById(R.id.lay_recognition_result);

layClassificationResult = findViewById(R.id.lay_classification_result);

rvRecognitionResult = findViewById(R.id.rv_recognition_result);

rvClassificationResult = findViewById(R.id.rv_classification_result);

//设置页面状态栏

setStatubar(this, R.color.white, true);

back(toolbar, true);

rxPermissions = new RxPermissions(this);

}

然后在initData中调用它。

@Override

public void initData(Bundle savedInstanceState) {

initView();

}

然后继承控件的点击监听回调

在这里插入图片描述

重写onClick方法。

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_web_picture://网络图片

break;

case R.id.btn_open_album://相册图片

break;

case R.id.btn_take_photo://拍照图片

break;

default:

break;

}

}

由于Token存在有效期的关系,因此不需要每一次都去获取,所以可以在第一次获取之后存入缓存,只要Token在有效期内都可以直接从缓存中获取,这样就可以少请求一次网络。

下面先写一个缓存的工具类。

在utils包下新增一个SPUtils类,里面的代码如下:

package com.llw.goodtrash.utils;

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();

}

}

然后在ImageInputActivity中写一个获取Token的方法,代码如下:

/**

  • 获取鉴权Token

*/

private String getAccessToken() {

String token = SPUtils.getString(Constant.TOKEN, null, this);

if (token == null) {

//访问API获取接口

mPresenter.getToken();

} else {

//则判断Token是否过期

if (isTokenExpired()) {

//过期

mPresenter.getToken();

} else {

accessToken = token;

}

}

return accessToken;

}

这里你的isTokenExpired()方法会报红,这是一个用来判断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方法中,通过mPresenter.getToken();发起了一个网络请求,返回的结果有成功和失败,成功后会有Token返回,失败了会有失败原因返回。

修改如下方法:

/**

  • 获取鉴权Token成功返回

  • @param response GetTokenResponse

*/

@Override

public void getTokenResponse(GetTokenResponse response) {

if (response != null) {

//鉴权Token

accessToken = response.getAccess_token();

//过期时间 秒

long expiresIn = response.getExpires_in();

//当前时间 秒

long currentTimeMillis = System.currentTimeMillis() / 1000;

//放入缓存

SPUtils.putString(Constant.TOKEN, accessToken, this);

SPUtils.putLong(Constant.GET_TOKEN_TIME, currentTimeMillis, this);

SPUtils.putLong(Constant.TOKEN_VALID_PERIOD, expiresIn, this);

} else {

showMsg(“Token为null”);

}

}

/**

  • 获取Token失败返回

  • @param throwable 异常

*/

@Override

public void getTokenFailed(Throwable throwable) {

Log.d(TAG, “Token获取失败:”+throwable.toString());

}

我已经写了注释了,那么你就知道这个方法是做什么的了。

五、识别网络图片


我的想法是当我点击这个网络图片的按钮时,页面出现一个输入框,当我输入完成之后,点击键盘的回车直接识别,虽后隐藏这个输入框,嗯,就是这样。

首先来写点击网络图片时的业务逻辑代码。

case R.id.btn_web_picture://网络图片

etImageUrl.setVisibility(View.VISIBLE);

etImageUrl.setOnKeyListener((view, keyCode, keyEvent) -> {

if (keyCode == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_UP) {

String webImageUrl = etImageUrl.getText().toString().trim();

String defaultWebImageUrl = “https://bce-baiyu.cdn.bcebos.com/14ce36d3d539b6004ef2e45fe050352ac65cb71e.jpeg”;

String imageUrl = “”.equals(webImageUrl) ?defaultWebImageUrl : webImageUrl;

//识别网络图片Url

showLoadingDialog();

Glide.with(context).load(imageUrl).into(ivPicture);

mPresenter.getDiscernResult(accessToken,null,imageUrl);

etImageUrl.setVisibility(View.GONE);

}

return false;

});

break;

在这里发起了一个图片识别的请求,下面来看返回的方法处理。

/**

  • 图片识别成功返回

  • @param response GetDiscernResultResponse

*/

@Override

public void getDiscernResultResponse(GetDiscernResultResponse response) {

if(response == null){

hideLoadingDialog();

showMsg(“未获得相应的识别结果”);

return;

}

ivPicture.setVisibility(View.VISIBLE);

List<GetDiscernResultResponse.ResultBean> result = response.getResult();

if (result != null && result.size() > 0) {

//显示识别结果

showDiscernResult(result);

} else {

hideLoadingDialog();

showMsg(“未获得相应的识别结果”);

}

}

/**

  • 图片识别成功失败

  • @param throwable 异常

*/

@Override

public void getDiscernResultFailed(Throwable throwable) {

Log.d(TAG, “图片识别失败:”+throwable.toString());

}

返回成功之后,如果数据不为空则显示要识别的图片,然后通过列表展示识别结果数据,

首先得有一个识别的结果列表item布局,item_distinguish_result_rv.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”

android:id=“@+id/item_distinguish_rv”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:layout_marginBottom=“1dp”

android:background=“#FFF”

android:foreground=“?attr/selectableItemBackground”

android:padding=“16dp”>

<TextView

android:id=“@+id/tv_keyword”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:textColor=“#000”

android:textSize=“16sp” />

<TextView

android:id=“@+id/tv_root”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_below=“@+id/tv_keyword”

android:layout_marginTop=“@dimen/dp_4”

android:textSize=“14sp” />

<TextView

android:id=“@+id/tv_score”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_alignParentEnd=“true” />

下面写适配器代码,在adapter下新建一个DiscernResultAdapter类,代码如下:

package com.llw.goodtrash.adapter;

import androidx.annotation.Nullable;

import com.chad.library.adapter.base.BaseQuickAdapter;

import com.chad.library.adapter.base.BaseViewHolder;

import com.llw.goodtrash.R;

import com.llw.goodtrash.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()))

.addOnClickListener(R.id.item_distinguish_rv);

}

}

适配器和列表item都写好了,下面回到ImageInputActivity中新增方法showDiscernResult(),代码如下:

/**

  • 显示识别的结果列表

  • @param result

*/

private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

{

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()))

.addOnClickListener(R.id.item_distinguish_rv);

}

}

适配器和列表item都写好了,下面回到ImageInputActivity中新增方法showDiscernResult(),代码如下:

/**

  • 显示识别的结果列表

  • @param result

*/

private void showDiscernResult(List<GetDiscernResultResponse.ResultBean> result) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-NreTwbfG-1713018282493)]

[外链图片转存中…(img-jdLpq69E-1713018282493)]

[外链图片转存中…(img-pTu2LeQV-1713018282494)]

[外链图片转存中…(img-RYwSUcRH-1713018282494)]

[外链图片转存中…(img-nXvVFqyv-1713018282494)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-hlRA0lBA-1713018282494)]

[外链图片转存中…(img-451RmZnZ-1713018282495)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值