上一篇:安卓&socket学习分享(三)
目录
本篇完成内容:
1)key热键操作
2)安卓端大量优化与升级
3)细节bug的修复与改进
idea端更新内容
Operator.java增加key命令操作
if (cmdHead.equals("key")) {
msgBackList = new KEY().exe(cmdBody); // 待实现的打开文件命令
return msgBackList;
}
增加VisualKeyMao.java
package lrz.tool;
import java.awt.event.KeyEvent;
import java.util.HashMap;
public class VisualKeyMap {
private static HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
private static final VisualKeyMap VISUAL_KEY_MAP = new VisualKeyMap();
private VisualKeyMap() {// 在私有的构造函数中对hashVisualKeyMap赋值,完成映射对象的赋值,该构造函数只在
// private static final VisualKeyMap VISUAL_KEY_MAP=new VisualKeyMap()静态变量中new出一次
// 无论调用多少次VisualKeyMap.getInstance()方法只返回VISUAL_KEY_MAP对象,而不会再调用构造函数new出对象
// 若要取客户端发送的"vk_space"对应的java.awt.event.KeyEvent.VK_SPACE值,则可通过VisualKeyMap.getVisualKey("vk_space")实现
hashMap.put("VK_0", KeyEvent.VK_0);//大写的Key,以方便录入,客户端发送大小写不区分
hashMap.put("VK_TAB", KeyEvent.VK_TAB);
hashMap.put("VK_ALT", KeyEvent.VK_ALT);
// 此处省略需要增加的映射操作
}
public static int getVisualKey(String key) {
//调用时只需VisualKeyMap.getVisualKey(String key)即可
return hashMap.get(key.toUpperCase());//把key转为大写
}
}
参考实验说明后,将之前使用接口DataTool实现的效果通过继承BaseOperator实现
OPN.java除了更改继承还要在返回结果添加如图
添加KEY.java来实现key热键操作
package lrz.data;
import lrz.base.BaseOperator;
import lrz.tool.VisualKeyMap;
import java.awt.*;
import java.util.ArrayList;
public class KEY extends BaseOperator {
private Robot robot;
@Override
public ArrayList<String> exe(String cmdBody) throws Exception {
// TODO Auto-generated method stub
// cmdBody,若有逗号,表示组合键,否则为单键
// 组合键示例:VK_ALT+VK_TAB,VK_TAB+VK_ALT 表示先按下alt键,再按下tab键,再释放tab键,再释放alt键
// 逗号前面的+表示按下键的顺序,逗号后面的+表示释放键的顺序
ArrayList<String> ackMsg = new ArrayList<String>();
robot=new Robot();
int splitIdx = cmdBody.indexOf(",");
//先判断键类型有无带","的,若有,则按下键的顺序和释放键的顺序由逗号前的“+”和逗号后的“+”来设置顺序
if (splitIdx < 1) {
int splitIdx2 = cmdBody.indexOf("+");//靠“+”来分割组合键
if(splitIdx2<1){
singleKeyPress(cmdBody);//没有“+”号表示单按键
}else{
simpleComboKeyPress(cmdBody);//组合键
}
}else{
String keyPressStr=cmdBody.substring(0, splitIdx);//取0-(spisplitIdx-1)索引构成的子字符串
String keyReleaseStr=cmdBody.substring(splitIdx+1);//取splitIdx+1到结尾的子字符串
comboKeyPress(keyPressStr,keyReleaseStr);
}
ackMsg.add("成功运行热键命令 key:"+cmdBody+">1>1>1>1>");
return ackMsg;
}
private void simpleComboKeyPress(String keyPressStr){
String[] keyPressArray = keyPressStr.split("\\+");
//split里的字符符合正则表达式规则, "+”是正则表达式的关键词,不能直接用,需要转义,而\本身是转义,所以需要\\来表示
for(int i=0;i<keyPressArray.length;i++){//按“+”的顺序按下按键
int keycode = VisualKeyMap.getVisualKey(keyPressArray[i]);
robot.keyPress(keycode);
}
for(int i=keyPressArray.length-1;i>=0;i--){//反序释放按键
int keycode = VisualKeyMap.getVisualKey(keyPressArray[i]);
robot.keyRelease(keycode);
}
}
private void comboKeyPress(String keyPressStr, String keyReleaseStr) {
// TODO Auto-generated method stub
String[] keyPressArray = keyPressStr.split("\\+");
String[] keyReleaseArray = keyReleaseStr.split("\\+");
for(int i=0;i<keyPressArray.length;i++){
int keycode = VisualKeyMap.getVisualKey(keyPressArray[i]);
robot.keyPress(keycode);
}
for(int i=0;i<keyReleaseArray.length;i++){
int keycode = VisualKeyMap.getVisualKey(keyReleaseArray[i]);
robot.keyRelease(keycode);
}
}
private void singleKeyPress(String cmdBody) {
// TODO Auto-generated method stub
int keycode = VisualKeyMap.getVisualKey(cmdBody);
robot.keyPress(keycode);
robot.keyRelease(keycode);
}
}
安卓端更新
MainActivity.java较多改动
package com.example.sockettest.app;
import androidx.appcompat.app.AppCompatActivity;
import androidx.annotation.NonNull;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.sockettest.R;
import com.example.sockettest.SocketClient;
import com.example.sockettest.data.CmdServerSetting;
import com.example.sockettest.data.NetFileData;
import com.example.sockettest.data.NetFileDataAdapter;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
SwipeRefreshLayout swipeRefreshLayout;
public static final String KEY_SERVER_ACK_MSG = "KEY_SERVER_ACK_MSG";
private Handler handler = null;
EditText dir;
ListView lv;
Button submit,back;
SocketClient socketClient=null;
String here;
private static final String KEY_HERE="here";
ArrayList<NetFileData> data;
ArrayList<String> in_data;
TextView here_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dir=findViewById(R.id.dir);
lv=findViewById(R.id.listview);
submit=findViewById(R.id.submit);
back=findViewById(R.id.back);
here_tv=findViewById(R.id.here);
initdata();
handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
Bundle data_bundle = msg.getData();
in_data=data_bundle.getStringArrayList(KEY_SERVER_ACK_MSG);
int fp=msg.arg2;
if(fp==SocketClient.SERVER_MSG_OK){
data=dataMaker();
NetFileDataAdapter netFileDataAdapter=new NetFileDataAdapter(MainActivity.this,data);
lv.setAdapter(netFileDataAdapter);
savehereshow();
} else {
String s=in_data.get(0);
int i=here.lastIndexOf("/");
here=here.substring(0,i);
Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
}
return false; }
});
submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
here=dir.getText().toString();
savedata();
getdata();
}
});
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int i=here.lastIndexOf("/");
int j=here.length();
if (i<j-1){
here=here+"/"+data.get(position).getFileName();
}else {
here=here+data.get(position).getFileName();
}
savedata();
getdata();
}
});
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int i=here.lastIndexOf("/");
if (i>6){
here=here.substring(0,i+1);
savedata();
getdata();
savehereshow();
}else {
Toast.makeText(MainActivity.this,"已在最外层位置",Toast.LENGTH_SHORT).show();
}
}
});
registerForContextMenu(lv);
}
private void getdata() {
socketClient=new SocketClient(CmdServerSetting.ip,CmdServerSetting.port,handler);
socketClient.work(here);
}
private void savehereshow(){
String s=here.substring(4);
here_tv.setText("当前位置:"+s);
}
private void savedata() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY_HERE,here);
editor.apply();
}
private void initdata() {
SharedPreferences sharedPreferences= PreferenceManager.getDefaultSharedPreferences(this);
here=sharedPreferences.getString(here,"d:/androidtest");
}
private ArrayList<NetFileData> dataMaker() {
ArrayList<NetFileData> dataResult=new ArrayList<>();
int i=in_data.size();
for (int j = 0; j <i ; j++) {
NetFileData netFileData=new NetFileData(in_data.get(j),here);
dataResult.add(netFileData);
}
return dataResult;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
getMenuInflater().inflate(R.menu.opt_menu,menu);
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
AdapterView.AdapterContextMenuInfo contextMenuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int pos=contextMenuInfo.position;
NetFileData netFileData=(NetFileData) lv.getAdapter().getItem(pos);//其中listView为显示文件列表的视图
switch(item.getItemId()){
case R.id.opt_show:// 弹出热键对话框
showDialog(netFileData);
break;
default :break;
}
return super.onContextItemSelected(item);
}
private void showDialog(NetFileData netFileData) {
AlertDialog.Builder bl = new AlertDialog.Builder(this);
bl.setTitle("基础操作");
View v = LayoutInflater.from(this).inflate(R.layout.dialog_view, null, false);
final Button bt1=v.findViewById(R.id.dialog_bt1);
bt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String s=here.substring(4);
socketClient=new SocketClient(CmdServerSetting.ip,CmdServerSetting.port,handler);
socketClient.work("opn:"+s+"/"+netFileData.getFileName());
}
});
bl.setView(v);
bl.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String s = "热键对话已关闭";
Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
}
});
bl.create().show();
}
}
新增NetFileData.java
package com.example.sockettest.data;
public class NetFileData {
private long fileSize = 0;// 文件长度应该long型数据,否则大于2GB的文件大小无法表达
private String fileName = "$error";// 文件名称,不含目录信息,默认值用于表示文件出错
private String filePath = ".\\";// 该文件对象所处的目录,默认值为当前相对目录
private String fileSizeStr = "文件夹";// 文件的大小,用字符串表示,能智能地选择B、KB、MB、GB来表达
private int fileType = 0;// fileType=0为文件,fileType=1为普通文件夹,fileType=2为盘符
private String fileModifiedDate = "1970-01-01 00:00:00";// 文件最近修改日期,默认值为1970年基准时间
public NetFileData(String fileInfo, String filePath2){
String[] data=fileInfo.split(">");
fileName=data[0];
fileModifiedDate=data[1];
fileSize=Long.parseLong(data[2]);
fileType=Integer.parseInt(data[3]);
if (fileSize>0){
if (fileSize>=1024 ){
long size_KB=fileSize/1024;
if (size_KB>=1024){
long size_MB=size_KB/1024;
if (size_MB>=1024){
double a=1.0;
double size_GB=size_MB/a/1024;
fileSizeStr=size_GB+" GB";
}else {
fileSizeStr=size_MB+" MB";
}
}else {
fileSizeStr=size_KB+" KB";
}
}else {
fileSizeStr="1 KB";
}
}
filePath=filePath2;
}
// public long getFileSize() {
// return fileSize;
// }
public String getFileName() {
return fileName;
}
public String getFilePath() {
return filePath;
}
public String getFileSizeStr() {
return fileSizeStr;
}
public int getFileType() {
return fileType;
}
public String getFileModifiedDate() {
return fileModifiedDate;
}
}
新增NeFileDataAdapter.java
package com.example.sockettest.data;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.sockettest.R;
import java.util.ArrayList;
public class NetFileDataAdapter extends ArrayAdapter<NetFileData> {
private Context context;
private ArrayList<NetFileData> list;
public NetFileDataAdapter(@NonNull Context context, ArrayList<NetFileData> list) {
super(context, android.R.layout.simple_list_item_1,list);
this.context = context;
this.list = list;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View v;
if(convertView==null){
v= LayoutInflater.from(context).inflate(R.layout.row_view,null,false);
}else{
v=convertView;
}
TextView name=v.findViewById(R.id.row_name);
TextView time=v.findViewById(R.id.row_time);
TextView size=v.findViewById(R.id.row_size);
final NetFileData data=list.get(position);
name.setText(data.getFileName());
time.setText(data.getFileModifiedDate());
size.setText(data.getFileSizeStr());
return v;
}
}
CmdServerSetting.java无变动
package com.example.sockettest.data;
public class CmdServerSetting {
public static String ip="192.168.43.12";
public static int port=8019;
}
SocketClient.java无变动
package com.example.sockettest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
public class SocketClient {
public static int SERVER_MSG_OK=0;//用于发送给句柄的消息类型,放在消息的arg2中,表示服务端正常
public static int SERVER_MSG_ERROR=1;//表示服务端出错
private int msgType;
private String ip;
private int port;
private ArrayList<String> cmd;
private int time_out=10000;
private int connect_timeout=10000;
private int transfer_timeout=10000;
private Handler handler;
private Socket socket;
public static final String KEY_SERVER_ACK_MSG = "KEY_SERVER_ACK_MSG";
private OutputStreamWriter writer;
private BufferedReader bufferedReader;
public SocketClient(String ip, int port, Handler handler) {
this.port = port;
this.ip = ip;
this.handler = handler;
}
private void connect() throws IOException {//连接服务端函数
InetSocketAddress address = new InetSocketAddress(ip, port);
socket = new Socket();
socket.connect(address, connect_timeout);
// if (!isDebug) {//若不处于调试模式,则设置socket数据传输超时
// socket.setSoTimeout(transfer_timeout);//设置传输数据的超时时间
// }
}
private void writeCmd(String cmd) throws IOException {
BufferedOutputStream os=new BufferedOutputStream(socket.getOutputStream());
writer=new OutputStreamWriter(os,"UTF-8");
writer.write("1\n");
writer.write(cmd+"\n");
writer.flush();
}
private ArrayList<String> readSocketMsg() throws IOException {
ArrayList<String> msgList=new ArrayList<>();
InputStreamReader isr=new InputStreamReader(socket.getInputStream(),"UTF-8");
bufferedReader=new BufferedReader(isr);
String numStr = bufferedReader.readLine();
int linNum = Integer.parseInt(numStr);
msgType=SERVER_MSG_ERROR;
if(linNum<1){
msgList.add("Receive empty message");
return msgList;
}
String status = bufferedReader.readLine();
if(status.equalsIgnoreCase("OK")){
msgType=SERVER_MSG_OK;
}else{
msgList.add(status);//将服务端的错误信息放入消息列表
}
for (int i = 1; i <linNum ; i++) {
String s = bufferedReader.readLine();
msgList.add(s);
}
return msgList;
}
private void close() throws IOException {
bufferedReader.close();
writer.close();
socket.close();
}
private void doCmdTask(String cmd){
ArrayList<String> msgList=new ArrayList<>();
try {
connect();
writeCmd(cmd);
msgList = readSocketMsg();
close();
} catch (IOException e) {
msgType=SERVER_MSG_ERROR;
msgList.add(e.toString());
e.printStackTrace();
}
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putStringArrayList(KEY_SERVER_ACK_MSG,msgList);
message.arg2=msgType;
message.setData(bundle);
handler.sendMessage(message);
}
public void work(final String cmd){
new Thread(new Runnable() {
@Override
public void run() {
doCmdTask(cmd);
}
}).start();
}
}
activity_main.xml有所变动
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="0dp"
android:layout_weight="5"
android:layout_height="wrap_content"
android:id="@+id/dir"
android:text="dir:d://"/>
<Button
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="submit"
android:id="@+id/submit"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:id="@+id/here"
android:text="当前位置"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/back"
android:text="back"/>
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/listview"/>
</LinearLayout>
新增dialog_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="打开文件"
android:id="@+id/dialog_bt1"/>
</LinearLayout>
新增row_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="name"
android:id="@+id/row_name"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="time"
android:id="@+id/row_time"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="size"
android:id="@+id/row_size"/>
</LinearLayout>
新增opt_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/opt_show"
android:title="基础操作" />
<item
android:id="@+id/opt_voice"
android:title="控制" />
<item
android:id="@+id/opt_set"
android:title="设置" />
</menu>
安卓端容易遇到问题,如果遇到报错或者跑不出来的问题也可下载源码尝试。
四次更新实现内容的安卓端源码已在gitee放入,可下载使用,注意下载后文件路径不能有中文。
https://gitee.com/li-run-ze/socketTest1
PS:需要进行内容扩充才能达到c档!
控制面板操作只实现了一个打开文件,需要自行扩充添加其他功能,例如key操作添加进去
至此实验指导中的一到七章中的内容已经基本实现,建议大家可以在原有基础上理解后可以添加多种功能,进行修改优化,创新,以上为个人根据实验内容以实现功能为主不断尝试,改写,优化升级后的代码分享,与原实验内容有所偏差。
断更提醒~~ 本次安卓课设系列更新估计是假期前的最后一更了,感谢浏览,祝学业有成。