TV输入法原理

在Android Latin输入法的基础上进行改写,该输入法不支持中文,处理流程如图:

第一次启动输入法服务时,根据键盘布局文件创建软键盘view,分别是全字母软键盘、数字软键盘以及特殊字符软键盘。在创建软键盘时,把每一个软键盘上的所有按键放到一个HashMap中,使每一个按键都分别与一个String型的数字对应。三个软键盘view对应的HashMap分别是qwertyMap、symbolsMap和symbolsShiftMap。

添加坐标类KeyCoordinates,该类的成员属性包括横坐标x和纵坐标y,成员方法包括设置横纵坐标setX()和setY(),以及获取横纵坐标getX()和getY()。

在输入法service类中,添加输入法状态标志位属性isInputMode,默认为false。isInputMode值为true时,软键盘上显示焦点,遥控器上方向键和确认键只用于软键盘上焦点的移动和焦点所在按键字符的输入,不具有系统原有功能,isInputMode为false时,软键盘上不会显示焦点,方向键和确认键具有系统原有功能,不用于对软件盘进行操作。

添加KeyCoordinates属性用来标记当前焦点所在位置,默认横纵坐标均为-1,即(-1,-1)。添加属性num和orinum,默认均为-1,其中num用来标记当前焦点所在按键的位置,orinum用来标记上一次焦点所在按键的位置。添加属性CHANGE_INPUTMODE_KEYCODE,该常量用来标记触发输入法状态切换的KeyEvent事件的keyCode值。

在进行输入时,软键盘打开的时候没有焦点。

按下遥控器上的输入法状态切换键后,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作。否则判断出该KeyEvent事件的keyCode等于CHANGE_INPUTMODE_KEYCODE,执行输入法状态切换操作,如果当前isInputMode为false,则将isInputMode设为true,KeyCoordinates设为(4,1),即软键盘中间按键对应的坐标位置,根据KeyCoordinates中x和y的值换算成对应的值存放到num中(如(0,1)换算成1。

软键盘不同,进行换算结果也不同,具体由软键盘按键个数和布局决定),再到当前软键盘对应的HashMap中取得num对应的按键,将焦点设到该按键上,并且将该按键的pressed状态设为true,高亮显示。

如果当前isInputMode为true,则将isInputMode设为false,将num和orinum的值均设为-1,KeyCoordinates设为(-1,-1),焦点所在按键的pressed状态设为false,取消焦点,软键盘上不再显示焦点。

按下遥控器上的左方向键,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。否则不返回给系统进行调用,只用于对软键盘进行操作,判断出该KeyEvent事件的keyCode等于KeyEvent.KEYCODE_DPAD_LEFT,然后再判断isInputMode的值,如果为false,不对输入法执行任何操作;如果为true,则对KeyCoordinates中的横坐标x进行操作,如果当前焦点所在的按键已经是最左边的按键,不进行任何操作,否则,x的值减1,然后将num的值赋给orinum。

根据KeyCoordinates中x和y的值换算成对应的值存放到num中,再到当前软键盘对应的HashMap中取得num对应的按键,将焦点移动到该按键上,设置该按键的pressed状态为true,高亮显示,到当前软键盘对应的HashMap中取得orinum对应的按键(即之前焦点所在的按键),将该按键pressed状态设为false,不再高亮显示。按下遥控器上其他方向键处理类似左方向键。

按下遥控器上的确认键,触发onKeyDown()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。否则不返回给系统进行调用,只用于对软键盘进行操作,判断出该KeyEvent事件的keyCode等于KeyEvent.KEYCODE_ENTER,然后再判断isInputMode的值,如果为false,不对输入法执行任何操作;如果为true,则调用方法onKey(),输入当前焦点所在的按键。

如果该按键为普通的字符键,则输入该按键代表的字符,如果该按键为shift键,则对软键盘执行shift操作,如果该按键为软键盘切换键(比如用于由全字母软键盘切换到数字软键盘),则将软键盘切换到另外一个软键盘,切换后,KeyCoordinates设为(4,1),即切换之后的软键盘中间按键对应的坐标位置,根据KeyCoordinates中x和y的值换算成对应的值存放到num中,再到当前软键盘对应的HashMap中取得num对应的按键,将焦点设到该按键上,并且将该按键的pressed状态设为true,高亮显示。

切换之前的软件盘上不再显示焦点,焦点所在按键的pressed状态设为false,如果该按键为删除键,则删除已输入的字符,如果该按键为确认键,则将执行系统调用的确认操作,按键的pressed状态设为false,软键盘上不再显示焦点,完成本次输入。

按下遥控器上的方向键或确认键,然后在释放的时候,触发onKeyUp()事件,首先判断软键盘此刻是否处于显示状态,即调用该软键盘view的isShown()方法,如果为false,不对输入法执行任何操作,而是返回给系统进行调用。如果为true,则不执行任何操作,也不返回给系统进行调用。当然要想支持中文,也可以修改android自带的google拼音输入法,原理同上。

LatinIME代码定向修改

添加功能

为输入法软键盘键子添加焦点(边框),并支持键盘上的方向键,在按上下左右方向键时,焦点跟着方向键移动;并支持键盘回车按钮,在按下回车键后,将软键盘对应键子内容输入到editor(编辑框)。

代码修改部分

1、在LatinKeyboardView中重写onDraw(Canvas canvas)

//BEGIN: add Keyboard focus
private Keyboard currentKeyboard;
private List<Key> keys = new ArrayList<Key>();
private int lastKeyIndex = 0;
private Key focusedKey;
private Rect rect;
/**
* override onDraw() method to draw rectangle focus round key
* @param canvas
*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentKeyboard = this.getKeyboard();
keys = currentKeyboard.mKeys;
Paint p = new Paint();
p.setColor(Color.CYAN);
p.setStyle(Style.STROKE);
p.setStrokeWidth(4.0f);
if(lastKeyIndex > keys.size()){
lastKeyIndex = keys.size() - 1;
}
focusedKey = keys.get(lastKeyIndex);
rect = new Rect(focusedKey.mX,focusedKey.mY+2,focusedKey.mX+focusedKey.mWidth,focusedKey.mY+focusedKey.mHeight);
if(this.isFocusFlag()){
canvas.drawRect(rect, p);
}
}/** provide lastKeyIndex access */
public int getLastKeyIndex() {
return lastKeyIndex;
}/** set key index */
public void setLastKeyIndex(int index) {
this.lastKeyIndex = index;
}/**The keyboard is get the focus*/
private boolean focusFlag = false;public boolean isFocusFlag() {
return focusFlag;
}public void setFocusFlag(boolean focusFlag) {
this.focusFlag = focusFlag;
}

2、在LatinIME类中重写onKeyDown(int keyCode, KeyEvent event),在方法中对方向键进行判断,并设计焦点移动过程。

/** Deal with remote control key event
*
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
hideFlag = false;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.getRepeatCount() == 0) {
if (mSuggestionsView !=null && mSuggestionsView.handleBack()) {
return true;
}
final LatinKeyboardViewkeyboardView = mKeyboardSwitcher
.getKeyboardView();
if (keyboardView != null && keyboardView.handleBack()) {
return true;
}
}
if (inputView != null && inputView.handleBack()) {
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
inputView.setLastKeyIndex(0);
} else {
int[] nearestKeyIndices= nCurrentKeyboard.getNearestKeys(
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
Key lastKey = nKeys.get(nLastKeyIndex);
for (int i = 0;i < nearestKeyIndices.length; i++) {
int index = nearestKeyIndices[i];
if (nLastKeyIndex < index) {
Key nearestKey = nKeys.get(index);
// tell right bound
if ((lastKey.mX+ lastKey.mWidth) > nearestKey.mX
&& (lastKey.mX + lastKey.mWidth) <= (nearestKey.mX + nearestKey.mWidth)) {
inputView.setLastKeyIndex(index);
break;
}
// ensure not bound outof softkeyboard
if (nearestKey.mX + nearestKey.mWidth
+ lastKey.mWidth > nCurrentKeyboard.mOccupiedWidth
&& lastKey.mX >= nearestKey.mX
&& lastKey.mX <(nearestKey.mX + nearestKey.mWidth)) {
Log.d(TAG, "if two " + index);
inputView.setLastKeyIndex(index);
break;
}
if (i == (nearestKeyIndices.length - 1)
&& (lastKey.mY + lastKey.mHeight + nearestKey.mHeight) <= nCurrentKeyboard.mOccupiedHeight) {
Log.d(TAG, "if three " + index);
inputView.setLastKeyIndex(index);
break;
}}
}// end for loop
}
if (inputView != null && inputView.isShown()) {
inputView.setFocusFlag(true);
inputView.invalidate();
}
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex <= 0){
inputView.setLastKeyIndex(nCurKeyboardKeyNums -1);
} else {
int[] nearestKeyIndices= nCurrentKeyboard.getNearestKeys(
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
// get current displayed
Key lastKey = nKeys.get(nLastKeyIndex);
for (int i = nearestKeyIndices.length -1; i >= 0; i--){
int index = nearestKeyIndices[i];
if (nLastKeyIndex > index) {
// get the nextkey
Key nearKey = nKeys.get(index);
// BEGIN: laskKey+1 :Because the last key better
// than his last one button x to small 1
if (lastKey.mX + 1 >= nearKey.mX
&& lastKey.mX <nearKey.mX + nearKey.mWidth) {
inputView.setLastKeyIndex(index);
break;
} else if (nearKey.mX -nearKey.mWidth < 0
&& lastKey.mX <= nearKey.mX) {
inputView.setLastKeyIndex(index);
break;
}
}
}// end for loop
}
if (inputView != null && inputView.isShown()) {
inputView.setFocusFlag(true);
inputView.invalidate();
}
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex <= 0){
inputView.setLastKeyIndex(nCurKeyboardKeyNums -1);
} else {
nLastKeyIndex--;
inputView.setLastKeyIndex(nLastKeyIndex);
}
inputView.setFocusFlag(true);
inputView.invalidate();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (inputView != null && inputView.isShown()) {
setFields();
if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
inputView.setLastKeyIndex(0);
} else {
nLastKeyIndex++;
inputView.setLastKeyIndex(nLastKeyIndex);
}
inputView.setFocusFlag(true);
inputView.invalidate();
return true;
}
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
Log.d(TAG, "[onKeyDown]::dpad_center========"+ keyCode);
if (inputView != null && inputView.isShown()) {
setFields();
final Key key = nKeys.get(nLastKeyIndex);
if (key.mOutputText != null) {
onTextInput(key.mOutputText);
}else if(mKeyboardSwitcher.isShiftedOrShiftLocked() && mKeyboardSwitcher.isShiftLocked()){
int curKeyCode = nKeys.get(nLastKeyIndex).mCode;
onCodeInput(curKeyCode, new int[] { curKeyCode },
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
}else if(mKeyboardSwitcher.isShiftedOrShiftLocked() && null != key.mHintLabel){
onTextInput(key.mHintLabel);
}else{
int curKeyCode = nKeys.get(nLastKeyIndex).mCode;
onCodeInput(curKeyCode, new int[] { curKeyCode },
nKeys.get(nLastKeyIndex).mX,
nKeys.get(nLastKeyIndex).mY);
}
return true;
}
break;
case KeyEvent.KEYCODE_DEL:
keyCode = -67;
onCodeInput(keyCode, new int[]{keyCode}, 0, 0);
break;
default:
if ((false == event.isCapsLockOn()) && (false == event.isShiftPressed())
&&((keyCode <= KeyEvent.KEYCODE_Z && keyCode >= KeyEvent.KEYCODE_A)))
{
if (event.getKeyCode() <= KeyEvent.KEYCODE_Z)
{
//Change to char key
keyCode = keyCode + 68;
onCodeInput(keyCode, new int[] { keyCode },
0,
0);
return true;
}
}
else if (keyCode == KeyEvent.KEYCODE_SPACE)
{
//Change to space key
keyCode = 32;
onCodeInput(keyCode, new int[] { keyCode },
0,
0);
return true;
}
//END
break;
}
return super.onKeyDown(keyCode,event);
}

LatinIME修改默认输入法语言

Android系统默认都是选择系统语言作为输入法,比如我们要用中文输入法,就需要切换系统语言为中文,或不勾选系统语言,主动勾选中文,但是我们怎么修改默认的语言输入法呢?

主要要关注3个模块代码:
(1) Setting源码
(2) SettingsProvider源码
(3) LatinIME输入法源码

方法一:修改默认输入法语言为英文和泰文

(1) 修改 packages\inputmethods\LatinIME\java\AndroidManifest.xml,增加

<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><receiver android:name="LatinImeReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

(2) 在packages\inputmethods\LatinIME\java\src\com\android\inputmethod\latin目录增加LatinImeReceiver.java文件,源码如下

package com.android.inputmethod.latin;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.provider.Settings;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.text.TextUtils;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager; public class LatinImeReceiver extends BroadcastReceiver {
private static final String TAG = LatinImeReceiver.class.getSimpleName();private static final String[] DEFAULT_LATIN_IME_LANGUAGES = {"en_US","th"};//默认开启输入法的语言@Override
public void onReceive(Context context, Intent intent) {
// Set the default input language at the system boot completed.if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Log.i(TAG,"onReceive:ACTION_BOOT_COMPLETED");
SharedPreferences sp = context.getSharedPreferences("default_input_language_config", Context.MODE_PRIVATE);
boolean hasSet = sp.getBoolean("has_set", false);if (!hasSet) {
setDefaultSubtypes(context);
sp.edit().putBoolean("has_set", true).commit();
}
}
}/**
* M: Set the default IME subtype.
*/private void setDefaultSubtypes(Context context) {
Log.i(TAG,"setDefaultSubtypes");
final String serviceName = "com.android.inputmethod.latin/.LatinIME";
final String currentPackageName = "com.android.inputmethod.latin";
final String enable = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
Log.i(TAG,"enable="+enable);//com.android.inputmethod.latin/.LatinIMEfinal InputMethodManager imm = (InputMethodManager) context.getSystemService( Context.INPUT_METHOD_SERVICE);
final StringBuilder builder = new StringBuilder();// Get sub type hash code
for (InputMethodInfo info : imm.getInputMethodList()) {
if (currentPackageName.equals(info.getPackageName())) {
Log.i(TAG,"info.getSubtypeCount()="+info.getSubtypeCount());//55
for (int i = 0; i < info.getSubtypeCount(); i++) {
final InputMethodSubtype subtype = info.getSubtypeAt(i);
final String locale = subtype.getLocale().toString();
Log.i(TAG,"locale="+locale);
if (isDefaultLocale(locale)) {
Log.i(TAG, "default enabled subtype locale = " + locale);
builder.append(';');
builder.append(subtype.hashCode());
}
}
break;
}
}// Insert the sub type
if (builder.length() > 0 && !TextUtils.isEmpty(enable)) {
final String subtype = builder.toString();
builder.setLength(0);
final int index = enable.indexOf(serviceName) + serviceName.length(); if (enable.length() > index) {
builder.append(enable.substring(0, index));
builder.append(subtype);
builder.append(enable.substring(index));
} else if (enable.length() == index) {
builder.append(enable);
builder.append(subtype);
} else {
return;
}
}
else {
Log.w(TAG, "Build Latin IME subtype failed: " + " builder length = " + builder.length() +
"; enable isEmpty :" + TextUtils.isEmpty(enable));
return;
}/*android/packages/inputmethods/LatinIME/java/res/xml/method.xml
-921088104;529847764分别代表en_US和th
*/
Log.i(TAG,"commoit:"+builder.toString());//com.android.inputmethod.latin/.LatinIME;-921088104;529847764// Commit the result
android.provider.Settings.Secure.putString(context.getContentResolver(),
android.provider.Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
String lastInputMethodId = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
Log.w(TAG, "DEFAULT_INPUT_METHOD = " + lastInputMethodId); //com.android.inputmethod.latin/.LatinIMEif(lastInputMethodId.equals(serviceName)) {
Log.w(TAG, "DEFAULT_INPUT_METHOD = com.android.inputmethod.latin/.LatinIME" );for (InputMethodInfo info : imm.getInputMethodList()) {
if (currentPackageName.equals(info.getPackageName())) {
for (int i = 0; i < info.getSubtypeCount(); i++) {
final InputMethodSubtype subtype = info.getSubtypeAt(i);
final String[] locales = DEFAULT_LATIN_IME_LANGUAGES;
Log.w(TAG, "i = " + i + ", locales[0] = " + locales[0]);
if((subtype.getLocale()).equals(locales[0])) {
Log.w(TAG, "putString " + subtype.hashCode());
android.provider.Settings.Secure.putInt(context.getContentResolver(),
android.provider.Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, subtype.hashCode());
}
}
}
}
}
}/**
* M: Check if the current locale is default or not.
*/
private boolean isDefaultLocale (String locale) {
final String[] locales = DEFAULT_LATIN_IME_LANGUAGES;for (String s : locales) {
if (s.equals(locale)) {
return true;
}
}return false;
}}

方法二:还有一种修改方案在amlogic验证是OK的
(1) 首先frameworks\base\packages\SettingsProvider\res\values\defaults.xml 增加下面语句

<string name="def_input_methods">com.android.inputmethod.latin/.LatinIME;529847764;-921088104</string>

意思是增加英文和泰文输入法,android/packages/inputmethods/LatinIME/java/res/xml/method.xml中有定义的-921088104;529847764分别代表en_US和th

(2) 然后在frameworks\base\packages\SettingsProvider\src\com\android\providers\settings\DatabaseHelper.java增加如下代码

loadStringSetting(stmt, Settings.Secure.ENABLED_INPUT_METHODS, R.string.def_input_methods);

frameworks/base/packages/SettingsProvider的作用

我们在调用android.provider.Settings修改一些设置时,Settings会调用真正的SettingsProvider去访问数据库。android把SettingsProvider的代码放在了frameworks/base/packages下面。

Android framework系统默认设置修改

修改Settings源码可修改系统设置项,Settings数据被存放于com.android.providers.settings/databases/settings.db 中,如果想修改系统启动后加载的默认值,一种方法是直接修改settings.db的值,另一种就是修改SettingsProvider默认值。

Settings应用能够配置Android系统的各种设置,这些设置的默认值都是由frameworks中的SettingsProvider从数据库中读取的,那么第一次开机的时候这些数据都是从哪儿来的呢?

frameworks/base/packages/SettingsProvider/res/values/defaults.xml这个文件就是用来存储Android系统的默认设置。

如果想定义defaults.xml中没有的,在这里添加后,还需修改frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java,加入自己的存储代码。

例如:
600000设置关屏超时时间的默认值
102 设置亮度的默认值
false设置是否允许安装非Market应用程序的默认值

Android TV定制输入法相关推荐

  1. Android TV 源码修改默认输入法

    前一阵子,应泰国客户需求,需要在Android TV系统定制一个多语言输入法,至少支持中.英.泰三种语言.拿到这个任务,对于至今还是小白的我来说,当然先去google一下有没有大神专门做过符合要求的输 ...

  2. android 定制输入法,QQ输入法Android 4.3全新升级 实现私人定制输入

    新一版QQ手机输入法来了, 针对Android平台每一个用户深度定制输入法.最新4.3版本除了对核心功能进行优化外,更带来了诸多新增功能:新增键盘大小调节,私人定制; 自定义皮肤可自选字体颜色,深度个 ...

  3. Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑

    原文:Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑 版权声明:我已委托"维权骑士"(rightknights.com)为我的文章进行维权行动.转载务必 ...

  4. Android TabLayout定制CustomView与ViewPager交互双向联动

     Android TabLayout定制CustomView与ViewPager交互双向联动 我之前写了一些关于Android Material Design TabLayout的文章, (1)& ...

  5. android tv 菜单键,Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑

    前言:关于<TV Metro界面(仿泰捷视频TV版)源码解析>由于都是相关代码,就不发公众号了,有兴趣的可以看链接:http://blog.csdn.net/hejjunlin/artic ...

  6. Android系统定制源码修改 - MTK平台

                                         Android系统定制源码修改 -  MTK平台  1.修改开机弹出欢迎使用SIM MTK工程/mediatek/packag ...

  7. android+tv盒子+主界面,x96max+ 盒子 与 CoreELEC系统配置(三)AndroidTV刷机记录

    x96max+ 盒子 与 CoreELEC系统配置(三)AndroidTV刷机记录 2020-03-19 22:05:49 39点赞 211收藏 59评论 本文简要记录一下AndroidTV的刷机过程 ...

  8. Android TV UI 设计指南 《一》

    随着国际市场上Google TV的发布和国内电视厂商纷纷推出定制Android TV的脚步,一时间智能电视平台成为了众多高科技企业争相抢占的新市场.较早的智能电视平台探索者有Google TV.App ...

  9. android tv长虹,首款智能系统长虹Android TV Plus问世

    12月上旬,长虹将推出B系列新品智能电视,而这个系列最大的亮点,便是它将全系列采用长虹自主研发的Android TV Plus操作系统. 随着智能电视潮流席卷国内的彩电市场,越来越多的用户已对电视有了 ...

最新文章

  1. Python3学习笔记-使用list和tuple
  2. php定时删除目录,shell定时删除指定目录下的文件
  3. ARM函数调用时参数传递规则
  4. mysql5.6编译_mysql5.6编译安装
  5. bzoj 2756奇怪的游戏
  6. kafka源码_终于看到有人把Kafka讲清楚了,阿里面试官推荐你看这份源码笔记
  7. 【转载】规则化和模型选择(Regularization and model selection)
  8. 用gpu跑_免费用GPU跑深度学习模型——如何获得极链AI云A100显卡
  9. 简单使用linux感受,linux小白说说用linux的感受
  10. jQuery 学习-DOM篇(二):jQuery 在 DOM 内部插入元素
  11. 【优化算法】多目标萤火虫算法(MOFA)【含Matlab源码 1595期】
  12. Java程序员年薪40W是什么水平?税前还是税后?
  13. 不相交轮换的乘积怎么求_伽罗华理论基础_刘长安.pdf_(12)(123)(14)不相交的乘积,8.将10次置换表互不相交的循环置换的乘积,并且求出。的逆与。的阶-教育文档类资源...
  14. 30分钟轻松在华为云上搭建网站应用(零基础手把手教学)
  15. 凡人修仙传jar_凡人的贝壳无法逃脱灵魂般的阴影
  16. 前嗅ForeSpider数据采集软件使用教程
  17. 永不断电的IPONE4
  18. vue中浏览器全屏和退出全屏
  19. safari 浏览器版本升级后提示“此网页出现问题,已重新载入网页” 解决办法
  20. BERT-QE:用于文档Rerank的上下文化查询扩展模型

热门文章

  1. deepmind_lab msys2就是在windows上面的linux
  2. 7-18关于预制件和添加图片
  3. 小程序常见的问题你一定遇到过!
  4. Angular2 中setTimeOut 回调函数未定义问题
  5. 操作系统页面置换算法之先进先出(FIFO)页面置换算法(C语言实现)
  6. 华为OD机试题【最差产品奖】用 C++ 编码,速通 (2023.Q1)
  7. 浩易南:能过这样的生活,就能年赚百万
  8. 对于目标文件系统 文件 过大
  9. swift编码出现Call can throw, but it is not marked with 'try' and the error is not handled错误的解决
  10. 南京某公司二期安居房工程大事记