android:orientation=“vertical”
tools:context=“.MainActivity”>
<androidx.appcompat.widget.Toolbar
android:layout_width=“match_parent”
android:layout_height=“?attr/actionBarSize”
android:background=“#FFF”
android:elevation=“3dp”>
<TextView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_gravity=“center”
android:text=“在线翻译”
android:textColor=“#000”
android:textSize=“18sp” />
</androidx.appcompat.widget.Toolbar>
<ScrollView
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:orientation=“vertical”
android:padding=“20dp”>
<LinearLayout
android:id=“@+id/before_lay”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:background=“#FFF”
android:foreground=“@drawable/stroke_gray”>
<org.angmarch.views.NiceSpinner
android:id=“@+id/sp_language”
android:layout_width=“match_parent”
android:layout_height=“48dp”
app:arrowTint=“#000”
app:textTint=“#000” />
<LinearLayout
android:id=“@+id/after_lay”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“center_vertical”
android:orientation=“horizontal”
android:visibility=“gone”>
<androidx.cardview.widget.CardView
android:layout_width=“0dp”
android:layout_height=“wrap_content”
android:layout_weight=“1”
android:background=“#FFF”
app:cardCornerRadius=“5dp”>
<TextView
android:id=“@+id/tv_from”
android:layout_width=“match_parent”
android:layout_height=“40dp”
android:background=“@drawable/stroke_black_radus”
android:gravity=“center”
android:textColor=“#000”
android:textSize=“14sp” />
</androidx.cardview.widget.CardView>
<ImageView
android:layout_width=“30dp”
android:layout_height=“30dp”
android:layout_marginLeft=“10dp”
android:layout_marginRight=“10dp”
android:src=“@mipmap/icon_change” />
<androidx.cardview.widget.CardView
android:layout_width=“0dp”
android:layout_height=“wrap_content”
android:layout_weight=“1”
android:background=“#FFF”
app:cardCornerRadius=“5dp”>
<TextView
android:id=“@+id/tv_to”
android:layout_width=“match_parent”
android:layout_height=“40dp”
android:background=“@drawable/stroke_black_radus”
android:gravity=“center”
android:textColor=“#000”
android:textSize=“14sp” />
</androidx.cardview.widget.CardView>
<RelativeLayout
android:layout_width=“match_parent”
android:layout_height=“150dp”
android:layout_marginTop=“20dp”
android:background=“#FFF”>
<EditText
android:id=“@+id/ed_content”
android:layout_width=“match_parent”
android:layout_height=“150dp”
android:background=“@drawable/stroke_gray”
android:gravity=“top”
android:hint=“请输入要翻译的文字内容”
android:maxLength=“6000”
android:paddingLeft=“10dp”
android:paddingTop=“10dp”
android:paddingRight=“30dp”
android:paddingBottom=“10dp”
android:textColor=“#000”
android:textColorLink=“#ABABAB”
android:textCursorDrawable=“@drawable/cursor_style” />
<ImageView
android:id=“@+id/iv_clear_tx”
android:layout_width=“20dp”
android:layout_height=“20dp”
android:layout_alignParentRight=“true”
android:layout_alignParentBottom=“true”
android:layout_marginRight=“8dp”
android:layout_marginBottom=“8dp”
android:src=“@mipmap/icon_delete”
android:visibility=“gone” />
<LinearLayout
android:visibility=“gone”
android:id=“@+id/result_lay”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“20dp”
android:orientation=“vertical”>
<TextView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:text=“翻译结果”
android:textColor=“#000”
android:textSize=“14sp” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“10dp”
android:background=“#FFF”
android:orientation=“vertical”>
<TextView
android:id=“@+id/tv_result”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:padding=“10dp”
android:textColor=“#000” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:gravity=“right”
android:orientation=“horizontal”>
<ImageView
android:id=“@+id/iv_copy_tx”
android:layout_width=“24dp”
android:layout_height=“24dp”
android:layout_marginRight=“4dp”
android:layout_marginBottom=“4dp”
android:padding=“4dp”
android:src=“@mipmap/icon_copy” />
<androidx.cardview.widget.CardView
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“20dp”
android:background=“#FFF”
app:cardCornerRadius=“5dp”>
<TextView
android:id=“@+id/tv_translation”
android:layout_width=“match_parent”
android:layout_height=“48dp”
android:background=“@drawable/stroke_black_radus”
android:foreground=“?android:attr/selectableItemBackground”
android:gravity=“center”
android:padding=“10dp”
android:text=“翻译”
android:textColor=“#000”
android:textColorLink=“#ABABAB”
android:textSize=“16sp” />
</androidx.cardview.widget.CardView>
这个布局里面,我现在隐藏了两块区域。第一块是翻译后的语言区域,第二块是翻译后的结果区域。运行的效果如下:
在MainActivity中,声明变量
private LinearLayout beforeLay;//翻译之前的布局
private NiceSpinner spLanguage;//语言选择下拉框
private LinearLayout afterLay;//翻译之后的布局
private TextView tvFrom;//翻译源语言
private TextView tvTo;//翻译目标语言
private EditText edContent;//输入框(要翻译的内容)
private ImageView ivClearTx;//清空输入框按钮
private TextView tvTranslation;//翻译
private LinearLayout resultLay;//翻译结果布局
private TextView tvResult;//翻译的结果
private ImageView ivCopyTx;//复制翻译的结果
private String fromLanguage = “auto”;//目标语言
private String toLanguage = “auto”;//翻译语言
private ClipboardManager myClipboard;//复制文本
private ClipData myClip; //剪辑数据
private String appId = “20201125000625305”;//APP ID 来源于百度翻译平台 请使用自己的
private String key = “6vjmDnNxypmebgbzKxul”;//秘钥 来源于百度翻译平台 请使用自己的
//配置初始数据
private List data = new LinkedList<>(Arrays.asList(
“自动检测语言”, “中文 → 英文”, “英文 → 中文”,
“中文 → 繁体中文”, “中文 → 粤语”, “中文 → 日语”,
“中文 → 韩语”, “中文 → 法语”, “中文 → 俄语”,
“中文 → 阿拉伯语”, "中文 → 西班牙语 ", “中文 → 意大利语”));
然后新写一个initView的方法。在这里对控件进行初始化并给予点击事件,并且数据赋值,
/**
- 初始化控件视图
*/
private void initView() {
//设置亮色状态栏模式 systemUiVisibility在Android11中弃用了,可以尝试一下。
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
//控件初始化
beforeLay = findViewById(R.id.before_lay);
spLanguage = findViewById(R.id.sp_language);
afterLay = findViewById(R.id.after_lay);
tvFrom = findViewById(R.id.tv_from);
tvTo = findViewById(R.id.tv_to);
edContent = findViewById(R.id.ed_content);
ivClearTx = findViewById(R.id.iv_clear_tx);
tvTranslation = findViewById(R.id.tv_translation);
resultLay = findViewById(R.id.result_lay);
tvResult = findViewById(R.id.tv_result);
ivCopyTx = findViewById(R.id.iv_copy_tx);
//点击时间
ivClearTx.setOnClickListener(this);
ivCopyTx.setOnClickListener(this);
tvTranslation.setOnClickListener(this);
//设置下拉数据
spLanguage.attachDataSource(data);
editTextListener();//输入框监听
spinnerListener();//下拉框选择监听
//获取系统粘贴板服务
myClipboard = (ClipboardManager) this.getSystemService(CLIPBOARD_SERVICE);
}
editTextListener方法代码:
/**
- 输入监听
*/
private void editTextListener() {
edContent.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
ivClearTx.setVisibility(View.VISIBLE);//显示清除按钮
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ivClearTx.setVisibility(View.VISIBLE);//显示清除按钮
}
@Override
public void afterTextChanged(Editable s) {
ivClearTx.setVisibility(View.VISIBLE);//显示清除按钮
String content = edContent.getText().toString().trim();
if (content.isEmpty()) {//为空
resultLay.setVisibility(View.GONE);
tvTranslation.setVisibility(View.VISIBLE);
beforeLay.setVisibility(View.VISIBLE);
afterLay.setVisibility(View.GONE);
ivClearTx.setVisibility(View.GONE);
}
}
});
}
spinnerListener方法代码如下:
/**
- 语言类型选择
*/
private void spinnerListener() {
spLanguage.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener() {
@Override
public void onItemSelected(NiceSpinner parent, View view, int position, long id) {
switch (position) {
case 0://自动检测
fromLanguage = “auto”;
toLanguage = fromLanguage;
break;
case 1://中文 → 英文
fromLanguage = “zh”;
toLanguage = “en”;
break;
case 2://英文 → 中文
fromLanguage = “en”;
toLanguage = “zh”;
break;
case 3://中文 → 繁体中文
fromLanguage = “zh”;
toLanguage = “cht”;
break;
case 4://中文 → 粤语
fromLanguage = “zh”;
toLanguage = “yue”;
break;
case 5://中文 → 日语
fromLanguage = “zh”;
toLanguage = “jp”;
break;
case 6://中文 → 韩语
fromLanguage = “zh”;
toLanguage = “kor”;
break;
case 7://中文 → 法语
fromLanguage = “zh”;
toLanguage = “fra”;
break;
case 8://中文 → 俄语
fromLanguage = “zh”;
toLanguage = “ru”;
break;
case 9://中文 → 阿拉伯语
fromLanguage = “zh”;
toLanguage = “ara”;
break;
case 10://中文 → 西班牙语
fromLanguage = “zh”;
toLanguage = “spa”;
break;
case 11://中文 → 意大利语
fromLanguage = “zh”;
toLanguage = “it”;
break;
default:
break;
}
}
});
}
同时要实现点击事件的监听,
鼠标点击这一行代码,然后使用快捷键,Alt + 回车
选择第一项,然后回车
点击OK,之后会重写onClick方法。这里不止一个控件,所以要根据view的id来判断才行。
/**
-
页面点击事件
-
@param v
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_clear_tx://清空输入框
edContent.setText(“”);//清除文本
ivClearTx.setVisibility(View.GONE);//清除数据之后隐藏按钮
break;
case R.id.iv_copy_tx://复制翻译后的结果
String inviteCode = tvResult.getText().toString();
myClip = ClipData.newPlainText(“text”, inviteCode);
myClipboard.setPrimaryClip(myClip);
showMsg(“已复制”);
break;
case R.id.tv_translation://翻译
translation();//翻译
break;
default:
break;
}
}
/**
-
Toast提示
-
@param msg
*/
private void showMsg(String msg) {
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
}
下面重点来看看这个翻译的方法,这里面的东西还挺多的。
既然是翻译首先获取到输入的文字,如下所示:
/**
- 翻译
*/
private void translation() {
//获取输入的内容
String inputTx = edContent.getText().toString().trim();
//判断输入内容是否为空
if (!inputTx.isEmpty() || !“”.equals(inputTx)) {//不为空
tvTranslation.setText(“翻译中…”);
tvTranslation.setEnabled(false);//不可更改,同样就无法点击
String salt = num(1);//随机数
//拼接一个字符串然后加密
String spliceStr = appId + inputTx + salt + key;//根据百度要求 拼接
String sign = stringToMD5(spliceStr);//将拼接好的字符串进行MD5加密 作为一个标识
//异步Get请求访问网络
asyncGet(inputTx, fromLanguage, toLanguage, salt, sign);
} else {//为空
showMsg(“请输入要翻译的内容!”);
}
}
这里先判断了输入框的值是否为null或者“”,不是则改变翻译按钮的文字显示,并且不可点击,给用户一种正在翻译的假象。之后就是生成一个随机数,这个是按照百度的要求来的,方法很简单,就不过多解释了。
/**
- 随机数 (根据百度的要求需要一个随机数)
*/
public static String num(int a) {
Random r = new Random(a);
int ran1 = 0;
for (int i = 0; i < 5; i++) {
ran1 = r.nextInt(100);
System.out.println(ran1);
}
return String.valueOf(ran1);
}
然后拼接一个字符串spliceStr,然后通过md5进行小写加密,加密后的值也是作为访问百度翻译API的一个参数。加密方法如下:
/**
-
将字符串转成MD5值
-
@param string 需要加密的内容
-
@return 加密后的字符串
*/
public static String stringToMD5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance(“MD5”).digest(string.getBytes(“UTF-8”));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append(“0”);
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
网络请求作为耗时操作,所以通常不会在主线程中进行,所以写一个异步Get请求访问网络的方法,传入刚才写进去的一些参数。
/**
-
异步Get请求
-
@param content 要翻译的内容
-
@param fromType 翻译源语言
-
@param toType 翻译后语言
-
@param salt 随机数
-
@param sign 标识
*/
private void asyncGet(String content, String fromType, String toType, String salt, String sign) {
//通用翻译API HTTP地址:
//http://api.fanyi.baidu.com/api/trans/vip/translate
//通用翻译API HTTPS地址:
//https://fanyi-api.baidu.com/api/trans/vip/translate
String httpStr = “http://api.fanyi.baidu.com/api/trans/vip/translate”;
String httpsStr = “https://fanyi-api.baidu.com/api/trans/vip/translate”;
//拼接请求的地址
String url = httpsStr +
“?appid=” + appId + “&q=” + content + “&from=” + fromType + “&to=” +
toType + “&salt=” + salt + “&sign=” + sign;
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求,可以不写
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
//异常返回
goToUIThread(e.toString(), 0);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//正常返回
goToUIThread(response.body().string(), 1);
}
});
}
这里就是OkHttp的简单使用了,而这里我写了两个访问地址,区别就是一个是https一个是http,不知道这两个区别的自行百度,没有解释的必要。你可能会问这个和Android有什么关系,别着急,会让你看到的。GET请求常规的操作就是把参数拼接到URL后面,我这里也是这么找的,
//拼接请求的地址
String url = httpsStr +
“?appid=” + appId + “&q=” + content + “&from=” + fromType + “&to=” +
toType + “&salt=” + salt + “&sign=” + sign;
URL的第一个参数要用?开头,后续参数使用&,其他的就是字符串的拼接了,一目了然。然后将地址放入到Request,里面完成请求体的构建,然后通过Call构建一个新的请求回调。在请求队列里监听请求的结果,结果只有两种,成功和失败。之前我是通过异步请求网络的,那么它的回调自然也不会是在主线程中,但是我又要在回调里面控制页面的UI。那么就需要切换到主线程或者UI线程中进行。于是你看到我在成功和失败的返回中都调用了goToUIThread,里面第一个参数作为一个Object对象,失败时传入错误信息,成功时传入返回数据。方法如下:
/**
-
接收到返回值后,回到UI线程操作页面变化
-
@param object 接收一个返回对象
-
@param key 表示正常还是异常
*/
private void goToUIThread(final Object object, final int key) {
//切换到主线程处理数据
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
tvTranslation.setText(“翻译”);
tvTranslation.setEnabled(true);
if (key == 0) {//异常返回
showMsg(“异常信息:” + object.toString());
Log.e(“MainActivity”,object.toString());
} else {//正常返回
//通过Gson 将 JSON字符串转为实体Bean
final TranslateResult result = new Gson().fromJson(object.toString(), TranslateResult.class);
tvTranslation.setVisibility(View.GONE);
//显示翻译的结果
tvResult.setText(result.getTrans_result().get(0).getDst());
resultLay.setVisibility(View.VISIBLE);
beforeLay.setVisibility(View.GONE);
afterLay.setVisibility(View.VISIBLE);
//翻译成功后的语言判断显示
initAfter(result.getFrom(), result.getTo());
}
}
});
}
这种写代码的方式虽然像是套娃一样,一层一层的,看起来好像很复杂的样子,但是其实是有一个思路串联起来的,由上到下,思路清晰就很好理解,我个人觉得比把所有代码写在一个方法里要好。而在上面的代码中,通过传进来的key来区别是成功还是失败。失败里面就是提示失败信息通过打印失败信息。成功里面,通过Gson将返回的JSON字符串转换成实体Bean。TranslateResult代码如下:
package com.llw.translate;
import java.util.List;
/**
-
翻译结果
-
@author llw
*/
public class TranslateResult {
/**
-
from : zh
-
to : en
-
trans_result : [{“src”:“早上”,“dst”:“morning”}]
*/
private String from;
private String to;
private List trans_result;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
一起互勉~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-8UFhxnyG-1713151758405)]
[外链图片转存中…(img-Be1IVOfb-1713151758406)]
[外链图片转存中…(img-Um6672u3-1713151758406)]
[外链图片转存中…(img-GAEcyheh-1713151758406)]
[外链图片转存中…(img-TE708c9p-1713151758407)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。
还有 高级架构技术进阶脑图、高级进阶架构资料 帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-s1ofMLOw-1713151758407)]
[外链图片转存中…(img-D9xtbLZN-1713151758407)]
一起互勉~
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!