安卓&socket学习分享(四)

上一篇:安卓&socket学习分享(三)

目录

idea端更新

安卓端更新


本篇完成内容:

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操作添加进去

至此实验指导中的一到七章中的内容已经基本实现,建议大家可以在原有基础上理解后可以添加多种功能,进行修改优化,创新,以上为个人根据实验内容以实现功能为主不断尝试,改写,优化升级后的代码分享,与原实验内容有所偏差。

断更提醒~~   本次安卓课设系列更新估计是假期前的最后一更了,感谢浏览,祝学业有成。

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值