Android外接掃碼器

最近做了一個(gè)關(guān)于Android設(shè)備Usb外接掃碼器的項(xiàng)目螟炫,在此記錄下根穷。
言歸正傳柏靶,首先掃碼器有這兩種模式:USB HID-KBW物邑,USB 虛擬串口溜哮。

  • USB HID-KBW:掃碼器會(huì)將掃描出來的內(nèi)容模擬成鍵盤事件,就是Android中就是KeyEvent里面對(duì)應(yīng)的常量(比如:KeyEvent.KEYCODE_A 表示小寫字母a色解,如果要表示大寫字母A茂嗓,就會(huì)組合KeyEvent.KEYCODE_SHIFT_LEFT或者KeyEvent.KEYCODE_SHIFT_RIGHT + KeyEvent.KEYCODE_A)。
    這種模式優(yōu)點(diǎn)是使用簡(jiǎn)單科阎,只要有焦點(diǎn)的文本輸入框述吸,系統(tǒng)會(huì)自動(dòng)將掃碼出的內(nèi)容填充到文本框內(nèi),缺點(diǎn)是不支持中文!

  • USB 虛擬串口:這種模式相對(duì)來說復(fù)雜點(diǎn)蝌矛,但也比較簡(jiǎn)單道批,Android系統(tǒng)屏蔽了底層串口通信(需要了解Android串口通信的可以看下android-serialport-api ),直接使用UsbDevice進(jìn)行通信,連接到UsbDevice入撒,讀取數(shù)據(jù)隆豹,好處是支持中文掃碼。

上代碼:
USB HID-KBW茅逮,這種模式我們可以自定義EditText對(duì)內(nèi)容進(jìn)行攔截璃赡,過濾出我們想要的內(nèi)容或者觸發(fā)我們的業(yè)務(wù)邏輯。自定義EditText很簡(jiǎn)單献雅,初始化的時(shí)候設(shè)置調(diào)用setOnKeyListener方法碉考,設(shè)置OnKeyListener,通過onKey方法返回true來攔截鍵盤事件挺身,至于攔截到的鍵盤事件通過KeyEvent的對(duì)應(yīng)常量轉(zhuǎn)換成相應(yīng)的字符(注意這里需要讓EditText獲取到焦點(diǎn)豆励,不然KeyEvent不會(huì)觸發(fā))。

public class CustomEditText extends android.support.v7.widget.AppCompatEditText {

    private ScanCodeListener codeListener;
    private StringBuffer buffer = new StringBuffer();

    public CustomEditText(Context context) {
        super(context);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private int preKeyCode = -1;

    private void init() {
        setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_UP) {
                    Log.d("ooo", "Code:" + keyCode + ", display int:" + (int) event.getDisplayLabel() + ", char:" + event.getDisplayLabel());

                    if (keyCode == KeyEvent.KEYCODE_ENTER) {
                        String str = buffer.toString();
                        buffer.setLength(0);
                        preKeyCode = -1;
                        if (codeListener != null) {
                            codeListener.handleCode(str);
                        }
                    } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
                        preKeyCode = KeyEvent.KEYCODE_SHIFT_RIGHT;
                    } else {
                        if (preKeyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
                            String shiftChar = SHIFT_KEY_MAP.get(keyCode);
                            if (shiftChar != null) {
                                buffer.append(shiftChar);
                            }
                        } else {
                            char label = event.getDisplayLabel();
                            if (label != 0) {
                                buffer.append(toLowCase(label));
                            }
                        }
                        preKeyCode = -1;
                    }
                }
                return true;
            }
        });
    }

    private char toLowCase(char c) {
        if (c >= 'A' && c <= 'Z') {
            c += 32;
            return c;
        }
        return c;
    }

    public void setScanCodeListener(ScanCodeListener codeListener) {
        this.codeListener = codeListener;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return super.dispatchKeyEvent(event);
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        boolean value = super.dispatchKeyEventPreIme(event);
        return value;
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, type);
    }

    @Override
    public void setOnKeyListener(OnKeyListener l) {
        super.setOnKeyListener(l);
    }

    public interface ScanCodeListener {
        void handleCode(String code);
    }

    static SparseArray<String> SHIFT_KEY_MAP = new SparseArray<>();

    static {

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_GRAVE, "~");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_0, ")");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_1, "!");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_2, "@");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_3, "#");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_4, "$");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_5, "%");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_6, "^");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_7, "&");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_8, "*");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_9, ")");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_MINUS, "_");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_EQUALS, "+");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_LEFT_BRACKET, "{");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_RIGHT_BRACKET, "}");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_BACKSLASH, "|");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SEMICOLON, ":");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_APOSTROPHE, "\"");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_COMMA, "<");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_PERIOD, ">");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_SLASH, "?");

        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_A, "A");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_B, "B");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_C, "C");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_D, "D");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_E, "E");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_F, "F");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_G, "G");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_H, "H");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_I, "I");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_J, "J");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_K, "K");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_L, "L");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_M, "M");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_N, "N");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_O, "O");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_P, "P");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Q, "Q");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_R, "R");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_S, "S");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_T, "T");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_U, "U");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_V, "V");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_W, "W");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_X, "X");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Y, "Y");
        SHIFT_KEY_MAP.put(KeyEvent.KEYCODE_Z, "Z");
    }
}

USB 虛擬串口瞒渠,這種模式稍稍復(fù)雜點(diǎn)良蒸,要自己去連接USB設(shè)備,讀取數(shù)據(jù)伍玖,其實(shí)也很簡(jiǎn)單

  1. 找到設(shè)備
  2. 設(shè)備授權(quán)
  3. 連接設(shè)備
  4. 讀取數(shù)據(jù)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final String ACTION_DEVICE_PERMISSION = "com.demo.USB_PERMISSION";

    private final int MY_PRODUCT_ID = 33286;//產(chǎn)品ID嫩痰,根據(jù)你的設(shè)備修改
    private final int MY_VENDOR_ID = 7851;  //廠商ID,根據(jù)你的設(shè)備修改
    
    private UsbManager usbManager;
    private TextView textView;
    
    private UsbEndpoint inUsbEndpoint;
    private UsbEndpoint outUsbEndpoint;
    private UsbInterface mUsbInterface;
    private UsbDeviceConnection mUsbDeviceConnection;

    private Thread thread;
    private Buff buff = new Buff(); //負(fù)責(zé)分割掃碼數(shù)據(jù)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        findViewById(R.id.list_usb_device).setOnClickListener(this);
        findViewById(R.id.connect).setOnClickListener(this);
        findViewById(R.id.read).setOnClickListener(this);
        findViewById(R.id.send).setOnClickListener(this);
        findViewById(R.id.clear).setOnClickListener(this);
        textView = (TextView) findViewById(R.id.device_info);

    }

    protected void onResume() {
        super.onResume();
        IntentFilter usbFilter = new IntentFilter();
        usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        usbFilter.addAction(ACTION_DEVICE_PERMISSION);
        registerReceiver(mUsbReceiver, usbFilter);
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mUsbReceiver);
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
                Log.d("xxx", "ACTION_USB_DEVICE_ATTACHED");
            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
                Log.d("xxx", "ACTION_USB_DEVICE_DETACHED");
            } else if (ACTION_DEVICE_PERMISSION.equals(action)) {
                UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    Toast.makeText(MainActivity.this, "已授權(quán)回調(diào)", Toast.LENGTH_SHORT).show();
                    if (device != null) {
                        initCommunication(device);
                    }
                } else {
                    Toast.makeText(MainActivity.this, "未授權(quán)回調(diào)", Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.list_usb_device) {
            HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
            if (deviceList != null) {
                Set<String> keySet = deviceList.keySet();
                StringBuffer stringBuffer = new StringBuffer();
                for (String key : keySet) {
                    stringBuffer.append(deviceList.get(key).toString() + "\n");
                    stringBuffer.append("\n\n");
                }
                textView.setText(stringBuffer.toString());
            } else {
                textView.setText("沒有設(shè)備");
            }
        } else if (v.getId() == R.id.connect) {
            HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
            if (deviceList.size() > 0) {
                for (UsbDevice device : deviceList.values()) {
                    if (MY_VENDOR_ID == device.getVendorId() && MY_PRODUCT_ID == device.getProductId()) {
                        if (usbManager.hasPermission(device)) {
                            initCommunication(device);
                            continue;
                        } else {
                            requestUsbPermission(device);
                        }
                        break;
                    }
                }
            }
        } else if (v.getId() == R.id.read) {
            startRead();
        } else if (v.getId() == R.id.send) {
            sendData();
        } else if (v.getId() == R.id.clear) {
            textView.setText("");
        }
    }

    //申請(qǐng)授權(quán)
    private void requestUsbPermission(UsbDevice device) {
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_DEVICE_PERMISSION), 0);
        usbManager.requestPermission(device, mPermissionIntent);
    }
    
    //開啟線程讀取數(shù)據(jù)
    private void startRead() {
        if (mUsbDeviceConnection == null && !mUsbDeviceConnection.claimInterface(mUsbInterface, true))
            return;
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                
                while (true) {
                    byte[] bytes = new byte[inUsbEndpoint.getMaxPacketSize()];
                    //讀取數(shù)據(jù), 這里注意最后一個(gè)參數(shù)為0時(shí) 為阻塞讀取窍箍,知道讀到數(shù)據(jù)為止
                    int ret = mUsbDeviceConnection.bulkTransfer(inUsbEndpoint, bytes, bytes.length, 0);
                    if (ret > 0) {
                        //這里因?yàn)閽叽a器的maxPacketSize很写摹(我的設(shè)備只有64byte),所以掃碼到的結(jié)果可能分多次讀取椰棘,
                        //所以這里將讀取到的數(shù)據(jù)丟到Buff里面纺棺,Buff負(fù)責(zé)分割成單條數(shù)據(jù)
                        buff.append(Arrays.copyOf(bytes, ret));
                        //分割獲取單條掃碼數(shù)據(jù)
                        byte[] code = buff.getCode();
                        if (code != null) {
                            final String str = new String(code);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    textView.setText(str);
                                }
                            });
                        }
                    } else {
                        Log.d("xxx", "Read empty data");
                    }
                }
            }
        });
        thread.start();
    }

    //初始化設(shè)備,找到對(duì)應(yīng)的通信接口
    private void initCommunication(UsbDevice device) {
        int interfaceCount = device.getInterfaceCount();
        for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {
            UsbInterface usbInterface = device.getInterface(interfaceIndex);
            if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())
                    && (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())
                    && UsbConstants.USB_CLASS_HID != usbInterface.getInterfaceClass()) {
                continue;
            }
            for (int i = 0; i < usbInterface.getEndpointCount(); i++) {

                UsbEndpoint ep = usbInterface.getEndpoint(i);
                if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                    if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
                        inUsbEndpoint = ep;
                    } else {
                        outUsbEndpoint = ep;
                    }
                }

            }

            if (null == inUsbEndpoint) {
                inUsbEndpoint = null;
                outUsbEndpoint = null;
                mUsbInterface = null;
            } else {
                //連接成功
                mUsbInterface = usbInterface;
                mUsbDeviceConnection = usbManager.openDevice(device);
                initConnection();
                break;
            }
        }
        if (mUsbDeviceConnection == null) {
            Toast.makeText(this, "連接失敗", Toast.LENGTH_SHORT).show();
        }
    }

    //初始化邪狞,設(shè)置比特率
    private void initConnection() {
        if (!mUsbDeviceConnection.claimInterface(mUsbInterface, true)) {
            return;
        }
        
        int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x22, 0x00, 0, null, 0, 0);
        setCdcBaudrate(9600);
    }

    private void setCdcBaudrate(int baudrate) {
        byte[] baudByte = new byte[4];

        baudByte[0] = (byte) (baudrate & 0x000000FF);
        baudByte[1] = (byte) ((baudrate & 0x0000FF00) >> 8);
        baudByte[2] = (byte) ((baudrate & 0x00FF0000) >> 16);
        baudByte[3] = (byte) ((baudrate & 0xFF000000) >> 24);
        int ret = mUsbDeviceConnection.controlTransfer(0x21, 0x20, 0, 0, new byte[]{
                baudByte[0], baudByte[1], baudByte[2], baudByte[3], 0x00, 0x00,
                0x08
        }, 7, 0);
    }

    //發(fā)送數(shù)據(jù)
    private void sendData() {
        byte[] buffer = "test data".getBytes();
        int ret = mUsbDeviceConnection.controlTransfer(UsbConstants.USB_DIR_OUT, 1, 1, 1, buffer, buffer.length, 1000);
        Toast.makeText(this, "send " + ret + " bytes data", Toast.LENGTH_SHORT).show();
    }
}

Buff 用于 分割掃碼數(shù)據(jù)祷蝌,我用的設(shè)備是以回車符(0X )+換行符(0X0A)為掃碼結(jié)束分隔符

public class Buff {
    private final int DELIMITER_1 = 0xd; //回車符
    private final int DELIMITER_2 = 0xa; //換行符
    private byte[] buffer;

    public void append(byte[] data) {
        if (buffer == null) {
            buffer = new byte[data.length];
            System.arraycopy(data, 0, buffer, 0, data.length);
        } else {
            byte[] temp = new byte[buffer.length + data.length];
            System.arraycopy(buffer, 0, temp, 0, buffer.length);
            System.arraycopy(data, 0, temp, buffer.length, data.length);
            buffer = temp;
        }
    }

    public byte[] getCode() {
        if (buffer == null)
            return null;
        if (buffer.length > 2) {
            boolean found = false;
            int i = 0;
            for (; i < buffer.length - 1; i++) {
                if (buffer[i] == DELIMITER_1 && buffer[i + 1] == DELIMITER_2) {
                    found = true;
                    break;
                }
            }
            if (found) {
                byte[] result = Arrays.copyOf(buffer, i);
                if (i < buffer.length - 2) {
                    buffer = Arrays.copyOfRange(buffer, i + 2, buffer.length);
                } else {
                    buffer = null;
                }
                return result;
            }
        }
        return null;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市帆卓,隨后出現(xiàn)的幾起案子巨朦,更是在濱河造成了極大的恐慌,老刑警劉巖剑令,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糊啡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吁津,警方通過查閱死者的電腦和手機(jī)棚蓄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梭依,你說我怎么就攤上這事挣柬。” “怎么了睛挚?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵邪蛔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我扎狱,道長(zhǎng)睬捶,這世上最難降的妖魔是什么沽讹? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任捎迫,我火速辦了婚禮统刮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘污抬。我一直安慰自己汞贸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布印机。 她就那樣靜靜地躺著矢腻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪射赛。 梳的紋絲不亂的頭發(fā)上多柑,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音楣责,去河邊找鬼竣灌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秆麸,可吹牛的內(nèi)容都是我干的初嘹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沮趣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼屯烦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兔毒,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤漫贞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后育叁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芍殖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年豪嗽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡龟梦,死狀恐怖隐锭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情计贰,我是刑警寧澤钦睡,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站躁倒,受9級(jí)特大地震影響荞怒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秧秉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一褐桌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧象迎,春花似錦荧嵌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汪厨,卻和暖如春逻族,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骄崩。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工聘鳞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人要拂。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓抠璃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親脱惰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子搏嗡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • afinalAfinal是一個(gè)android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,429評(píng)論 2 45
  • 先說一下這篇文章里面的內(nèi)容:TCP 客戶端, 自定義對(duì)話框, 自定義按鈕, ProgressBar豎直顯示, 重力...
    楊奉武閱讀 3,301評(píng)論 0 3
  • 這是一篇很現(xiàn)實(shí)的小說拉一,女人和兄弟采盒,我相信有很多人會(huì)選擇兄弟,但是最后的做法卻讓人擔(dān)心蔚润。 讓我一起看一看凌晨的故事磅氨,...
    小破丐閱讀 593評(píng)論 0 1
  • 2017年1月7日,雨一直下嫡纠,氣氛還算融洽烦租。 穿花蝴蝶般繞過迷宮似的延赌、比世博入口還曲折的博覽中心入口,進(jìn)入了N2展...
    昊昊爸閱讀 413評(píng)論 0 1