Android--连接USB打印机

访客机或消费机等设备有连接打印机打印小票的需求,本篇记录一下设备连接USB打印机打印小票内容的功能实现。

其中涉及的工具类包括Other.java、Command.java、PrinterCommand.java和PrinterController.java

连接USB打印机

1. Other.java类

该工具类中只包含一个方法,用于将byteArrays数据转换为Bytes数据。

public class Other {
    public static byte[] byteArraysToBytes(byte[][] data) {
        int length = 0;

        for (byte[] datum : data) {
            length += datum.length;
        }

        byte[] send = new byte[length];
        int k = 0;

        for (byte[] datum : data) {
            for (byte b : datum) {
                send[k++] = b;
            }
        }

        return send;
    }
}

2. Command.java类

该类主要定义打印机所需的指令。

/**
 * 打印机指令
 */
public class Command {

    //打印机初始化
    public static byte[] ESC_Init = new byte[] {0x1b, 0x40 };

    //实时状态传送指令
    public static byte[] DLE_eot = new byte[] {0x10, 0x04, 0x04 };
    //实时状态传送指令 查询1 打印机
    public static final byte[] DLE_eot1 = new byte[] {0x10, 0x04, 0x01};
    //实时状态传送指令 查询2 脱机
    public static final byte[] DLE_eot2 = new byte[] {0x10, 0x04, 0x02};
    //实时状态传送指令 查询3 错误
    public static final byte[] DLE_eot3 = new byte[] {0x10, 0x04, 0x03};
    //实时状态传送指令 查询4 卷纸传感器
    public static final byte[] DLE_eot4 = new byte[] {0x10, 0x04, 0x04};

    //获取打印机打印状态  测试
    public static final byte[] ESC_STATUS = new byte[] {0x1B, 0x76};

    //设置对齐模式
    public static byte[] ESC_Align = new byte[] {0x1b, 0x61, 0x00 };
    //设置字体加粗
    public static final byte[] ESC_BOLD = new byte[] {0x1b, 0x45,0x01};

    //横向放大
    public static final byte[] ESC_GS_H = new byte[] {0x1b, 0x55, 0x00};
    //纵向放大
    public static final byte[] ESC_GS_O = new byte[] {0x1b, 0x56, 0x00};
    //横向纵向放大
    public static final byte[] ESC_GS_O_H = new byte[] {0x1b, 0x57, 0x00};

    //换行
    public static final byte[] ESC_WRAP= new byte[] {0x0A};
    //回车
    public static final byte[] ESC_ENTER= new byte[] {0x0D};
    //走纸N行, 最后一位是行数,默认一行
    public static final byte[] ESC_Paper = new byte[] {0x1B, 0x64, 0x01};

    //简体中文字符集
    public static final byte[] ESC_Chinese = new byte[] {0x1c, 0x26, 0x1b, 0x74, 0x00};
}

3. PrinterCommand.java类

该类提供打印功能,包括打印机初始化、设置打印格式等功能。

public class PrinterCommand {
    /**
     * 打印机初始化
     */
    public byte[] POS_Set_PrtInit() {

        return Other.byteArraysToBytes(new byte[][]{Command.ESC_Init});
    }

    /**
     * 设置对齐模式
     *
     * @param align 0 左对齐, 1 居中, 2 右对齐
     * @return 打印机字节
     */
    public byte[] POS_S_Align(int align) {
        if (align < 0 || align > 2) {
            return null;
        }
        byte[] data = Command.ESC_Align;
        data[2] = (byte) align;
        return data;
    }

    /**
     * 换行
     * @param count 换行行数1-10
     * @return
     */
    public  byte[] POS_S_PrintLine(int count){
        if(count <= 0) count = 1;
        if(count >= 10) count = 10;

        byte[] data = Command.ESC_Paper;
        data[2] = (byte) count;
        return data;
    }

    /**
     * 横向放大
     * @param font_size 放大尺寸
     * @return
     */
    public byte[] POS_S_GS_H(int font_size) {
        if (font_size <= 0) font_size = 0;
        if (font_size >= 7) font_size = 7;
        byte[] data = Command.ESC_GS_H;
        data[2] = (byte) (font_size );
        return data;
    }

    /**
     * 纵向放大
     * @param font_size 放大尺寸
     * @return
     */
    public byte[] POS_S_GS_O(int font_size) {
        if (font_size <= 0) font_size = 0;
        if (font_size >= 7) font_size = 7;
        byte[] data = Command.ESC_GS_O;
        data[2] = (byte) (font_size );
        return data;
    }

    /**
     * 横向纵向同时放大
     * @param font_size 放大尺寸
     * @return
     */
    public  byte[] POS_S_GS_O_H(int font_size) {
        if (font_size <= 0) font_size = 0;
        if (font_size >= 7) font_size = 7;
        byte[] data = Command.ESC_GS_O_H;
        data[2] = (byte) ( font_size );
        return data;
    }

    /**
     * 加粗
     * @return
     */
    public byte[] Set_Bold(){
        byte[] data = Command.ESC_BOLD;
        data[2] = (byte) (1);
        return data;
    }

    /**
     * 换行
     * @return
     */
    public byte[] POS_Set_Enter(){
        return Command.ESC_ENTER;
    }

}

4. PrinterController.java类

该类为打印控制类,提供了打印机连接、打印内容、监听usb打印机连接状态等接口。

public class PrinterController extends BroadcastReceiver {

    private static final String TAG = "PrinterController";
    private Context appContext;
    private UsbManager mUsbManager;
    // 我们的发送端、打印机的接收端
    private UsbEndpoint epOut;
    // 我们的读取端、打印机的发送端
    private UsbEndpoint epIn;
    private UsbInterface usbIf;
    private UsbDeviceConnection conn;
    private UsbDevice dev;

    private static final int LINE_BYTE_SIZE = 48;

    public static final String PRINTER = "printer";
    public static final String MICRO_PRINTER = "micro printer";
    public static final String FMT_YYYY_MM_DD_HH_MM2 = "yyyy-MM-dd HH:mm";
    public static final String ACTION_USB_PERMISSION = "com.zj.usbconn.USB";

    private static final PrinterController INSTANCE = new PrinterController();

    public PrinterController() {}

    public static PrinterController getInstance() {
        return INSTANCE;
    }

    public void getPrinterContext(Context context) {
        appContext = context;
        mUsbManager = (UsbManager) appContext.getSystemService(Context.USB_SERVICE);
    }

    public final synchronized void connect(UsbDevice dev) {
        close();
        PrinterController.getInstance().dev = dev;
        if (!this.isHasPermission(dev)) {
            this.getPermission(dev);
            Toast.makeText(appContext, "打印机无权限", Toast.LENGTH_SHORT).show();
            Log.i(TAG, "打印机无权限getPermission");
        } else {
            Toast.makeText(appContext, "打印机连接成功", Toast.LENGTH_SHORT).show();
            Log.i(TAG, "打印机连接成功connect");
        }
    }

    public final synchronized void tryConnect() {
        close();
        boolean hasPrinters = false;
        for (UsbDevice device : mUsbManager.getDeviceList().values()) {
            String name = device.getProductName();
            if (name == null || name.isEmpty()) {
                continue;
            }
            name = name.toLowerCase(Locale.CHINA);
            if (name.contains(PRINTER) || name.contains(MICRO_PRINTER)) {
                hasPrinters = true;
                connect(device);
                break;
            }
        }
        if (!hasPrinters){
            Log.d(TAG, "未搜索到USB打印机!");
            Toast.makeText(appContext, "未搜索到USB打印机!", Toast.LENGTH_SHORT).show();
        }
    }

    public final synchronized boolean isHasPermission(UsbDevice dev) {
        return mUsbManager.hasPermission(dev);
    }

    public final synchronized void getPermission(UsbDevice dev) {
        if (!isHasPermission(dev)) {
            PendingIntent pi = PendingIntent.getBroadcast(appContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
            mUsbManager.requestPermission(dev, pi);
            Toast.makeText(appContext, "打印机请求权限!", Toast.LENGTH_SHORT).show();
            Log.i(TAG, "打印机请求权限requestPermission");
        } else {
            Toast.makeText(appContext, "打印机连接成功!", Toast.LENGTH_SHORT).show();
            Log.i(TAG, "打印机连接成功");
        }
    }

    public final synchronized void sendByte(byte[] bits) {
        if (bits == null) {
            return;
        }

        // 打印机正常
        if (epOut != null && usbIf != null && conn != null) {
            conn.bulkTransfer(epOut, bits, bits.length, 0);
            return;
        }

        Log.d(TAG, "PrinterController sendByte: initDeviceUsb()");
        UsbDeviceConnection tmpConn = initDeviceUsb();
        if (tmpConn != null && tmpConn.claimInterface(usbIf, true)) {
            tmpConn.bulkTransfer(epOut, bits, bits.length, 0);
        }
    }

    public UsbDeviceConnection initDeviceUsb() {
        if (conn == null) {
            conn = mUsbManager.openDevice(dev);
        }

        if (dev == null || dev.getInterfaceCount() == 0) {
            return null;
        }

        usbIf = dev.getInterface(0);
        if (usbIf == null) {
            return null;
        }

        if (usbIf.getEndpointCount() == 0) {
            return null;
        }

        Log.d(TAG, "PrinterController sendByte: i in 0 until it.endpointCount");
        for (int i = 0; i < usbIf.getEndpointCount(); i++) {
            UsbEndpoint endpoint = usbIf.getEndpoint(i);
            if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK && endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
                epOut = endpoint;
                break;
            }
        }
        return conn;
    }

    private final synchronized void sendByteDirectly(byte[] bits) {
        UsbDeviceConnection tmpConn = initDeviceUsb();
        if (tmpConn == null) {
            return;
        }
        if (tmpConn.claimInterface(usbIf, true)) { // 获取USB设备接口的控制权
            tmpConn.bulkTransfer(epOut, bits, bits.length, 0);  // 批量数据传输
        }
    }

    // 读打印机状态, 对海外打印机无用,只对国内打印机有用
    // 不能频繁查询,会导致打印机无响应
    public int readStatus(byte[] command) {
        sendByteDirectly(command);
        int count = (usbIf != null ? usbIf.getEndpointCount() : -1);
        Log.d(TAG, "PrinterController readStatus: " + count);
        if (epIn == null) {
            for (int i = 0; i < count; i++) {
                UsbEndpoint ep = usbIf != null ? usbIf.getEndpoint(i) : null;
                if (ep != null && ep.getType() != UsbConstants.USB_ENDPOINT_XFER_BULK) return -1;
                if (ep != null && ep.getDirection()!= UsbConstants.USB_DIR_IN) continue;    //不是读端就跳过,一共2个端口,一个读一个写
                //读端初始化
                if (epIn == null) {
                    epIn = ep;
                    Log.d(TAG, "PrinterController read:epIn init");
                }
                break;
            }
        }

        UsbEndpoint tmpEpIn = epIn;
        UsbDeviceConnection tmpConn = conn;
        if (tmpEpIn == null || tmpConn == null) return -1;

        byte[] retData = new byte[64];
        int readLen = tmpConn.bulkTransfer(tmpEpIn, retData, retData.length, 3000);
        Log.d(TAG, "PrinterController read:readLen " + readLen);

        if (readLen == -1) return -1;
        Log.d(TAG, "PrinterController read:retData != -1");

        byte[] realData = new byte[65];
        System.arraycopy(retData, 0, realData, 0, retData.length);
        Log.d(TAG, "PrinterController read:readLen " + realData[0]);

        return realData[0];
    }

    // 一行打印2段内容
    public String printTwoData(String leftText, String rightText) {

        int leftTextLength = leftText.getBytes().length;
        int rightTextLength = rightText.getBytes().length;

        StringBuilder sb = new StringBuilder();
        sb.append(leftText);

        // 计算两侧文字中间的空格
        int marginBetweenMiddleAndRight = LINE_BYTE_SIZE - leftTextLength - rightTextLength;
        for (int i = 0; i < marginBetweenMiddleAndRight; i++) {
            sb.append(" ");
        }
        sb.append(rightText);

        // 处理信息过长问题
        int outCome = (leftTextLength + rightTextLength) / LINE_BYTE_SIZE;  // 结果
        int remainder = (leftTextLength + rightTextLength) % LINE_BYTE_SIZE;// 余数
        // 结果不为0并且还有余数,表明换行了,此时需要在后面加空白
        // 计算出离右边的距离
        int marginRightBlank = (outCome > 0 && remainder > 0) ? (((outCome + 1) * LINE_BYTE_SIZE) - leftTextLength - rightTextLength) : 0;
        for (int i = 0; i < marginRightBlank; i++) {
            sb.append(" ");
        }
        return sb.toString();
    }

    // 一行打印3段内容
    public String printThreeData(String leftText, String centerText, String rightText) {

        int leftTextLength = leftText.getBytes().length;
        int centerTextLength = centerText.getBytes().length;
        int rightTextLength = rightText.getBytes().length;

        StringBuilder sb = new StringBuilder();
        sb.append(leftText);

        // 计算两侧文字中间的空格
        int marginBetweenMiddleAndRight = LINE_BYTE_SIZE - leftTextLength - centerTextLength - rightTextLength;
        for (int i = 0; i < marginBetweenMiddleAndRight/2; i++) {
            sb.append(" ");
        }
        sb.append(centerText);

        for (int i = 0; i < marginBetweenMiddleAndRight/2; i++) {
            sb.append(" ");
        }
        sb.append(rightText);

        // 处理信息过长问题
        int outCome = (leftTextLength + centerTextLength + rightTextLength) / LINE_BYTE_SIZE;  // 结果
        int remainder = (leftTextLength + centerTextLength + rightTextLength) % LINE_BYTE_SIZE;// 余数
        // 结果不为0并且还有余数,表明换行了,此时需要在后面加空白
        // 计算出离右边的距离
        int marginRightBlank = (outCome > 0 && remainder > 0) ? (((outCome + 1) * LINE_BYTE_SIZE) - leftTextLength - centerTextLength - rightTextLength) : 0;
        for (int i = 0; i < marginRightBlank; i++) {
            sb.append(" ");
        }
        return sb.toString();
    }

    public static String date2String(Date date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(date);
    }

    public final synchronized void close() {
        if (conn != null) {
            conn.close();
            epOut = null;
            epIn = null;
            usbIf = null;
            conn = null;
            dev = null;
        }
    }

    // 监听usb设备连接状态广播,
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive intent = " + intent);
        // USB设备断开广播
        if (intent.getAction() == UsbManager.ACTION_USB_DEVICE_DETACHED){
            Toast.makeText(appContext, "打印机连接断开", Toast.LENGTH_SHORT).show();
        }
    }

}

5. 打印机使用

添加好打印工具类后就可以再MainActivity中连接、使用打印机了。MainActivity中添加了两个按钮,分别用于实现连接打印机和开始打印2个功能。

<MainActivity.java>

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "PrintingActivity";

    private PrinterController printer;
    private final PrinterCommand commands = new PrinterCommand();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化打印工具类
        printer = PrinterController.getInstance();
        printer.getPrinterContext(getApplicationContext());

        // 动态注册 usb设备断开 广播
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        registerReceiver(printer, intentFilter);

        initView();
    }

    private void initView(){
        // 连接打印机
        findViewById(R.id.connect_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                initPrint();
            }
        });
        // 开始打印
        findViewById(R.id.print_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startPrinting();
            }
        });

    }

    // 连接打印机
    private void initPrint(){
        printer.close();
        printer.tryConnect();
    }

    // 开始打印
    private void startPrinting(){
        // 1. 打印之前先建立连接
        printer.tryConnect();

        // 2. 检查状态是否正常
        int status = printer.readStatus(Command.DLE_eot4);
        if (status != 18){
            Toast.makeText(this, "打印机异常,请检查打印机!", Toast.LENGTH_SHORT).show();
            Log.e(TAG,"printer error : status = " + status);
            return;
        }else {
            Toast.makeText(this, "打印机正常!", Toast.LENGTH_SHORT).show();
            Log.e(TAG,"printer over : status = " + status);
        }

        // 3. 自定义打印内容
        customPrint();

        // 4. 关闭打印机
        printer.close();
    }

    /**
     * 自定义打印格式和打印内容
     */
    private void customPrint(){
        printOneData("北京稻香村", 1, false, true, "all", 2, true, 2);
        printTwoData("打印时间:", printer.date2String(new Date(), printer.FMT_YYYY_MM_DD_HH_MM2), 1, false, true, "all", 1, true, 1);
        printOneData("-----------------------------------------------", 1, false, false, "", 0, true, 1);
        printThreeData("单价", "数量", "金额", 1, false, false, "", 0, true, 1);
        printOneData("-----------------------------------------------", 1, false, false, "", 0, true, 1);
        printThreeData("蛋黄酥", "1", "¥50.00", 1, false, false, "", 0, true, 1);
        printOneData("-----------------------------------------------", 1, false, false, "", 0, true, 1);
        printTwoData("实收金额", "¥50.00", 1, true, false, "", 0, true, 4);
    }

    /**
     * 设置打印内容:一行只有1个打印内容
     * @param text 打印的文本
     * @param align 对齐模式       0#左对齐, 1#居中, 2#右对齐
     * @param isBold 是否加粗
     * @param isEnlarge 是否放大
     * @param direction 放大方向    horizontal#水平放大, vertical#垂直放大, all#整体放大
     * @param multiple 放大倍数     [0,7]
     * @param isEnter 是否回车
     * @param enterLine 换行数     [1.10]
     */
    private void printOneData(String text, int align, boolean isBold, boolean isEnlarge, String direction, int multiple, boolean isEnter, int enterLine){

        printer.sendByte(commands.POS_Set_PrtInit());   // 初始化格式
        printer.sendByte(commands.POS_S_Align(align));  // 对齐模式
        if (isBold){
            printer.sendByte(commands.Set_Bold());      //加粗
        }
        if (isEnlarge){
            if ("horizontal".equals(direction)){
                printer.sendByte(commands.POS_S_GS_H(multiple));   // 横向放大
            }else if ("vertical".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O(multiple));   // 纵向放大
            }else if ("all".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O_H(multiple)); // 放大字体
            }
        }
        printer.sendByte(text.getBytes(Charset.forName("GBK")));  // 设置打印内容
//        printer.sendByte(commands.POS_Set_Enter());  // 回车
        if (isEnter) {
            printer.sendByte(commands.POS_S_PrintLine(enterLine));  // 换行
        }
    }

    /**
     * 设置打印内容:一行有2个打印内容
     * @param leftText 左侧打印的文本
     * @param rightText 右侧打印的文本
     * @param align 对齐模式       0#左对齐, 1#居中, 2#右对齐
     * @param isBold 是否加粗
     * @param isEnlarge 是否放大
     * @param direction 放大方向    horizontal#水平放大, vertical#垂直放大, all#整体放大
     * @param multiple 放大倍数     [0,7]
     * @param isEnter 是否回车
     * @param enterLine 换行数     [1.10]
     */
    private void printTwoData(String leftText, String rightText, int align, boolean isBold, boolean isEnlarge, String direction, int multiple, boolean isEnter, int enterLine){

        printer.sendByte(commands.POS_Set_PrtInit());   // 初始化格式
        printer.sendByte(commands.POS_S_Align(align));  // 对齐模式
        if (isBold){
            printer.sendByte(commands.Set_Bold());      //加粗
        }
        if (isEnlarge){
            if ("horizontal".equals(direction)){
                printer.sendByte(commands.POS_S_GS_H(multiple));   // 横向放大
            }else if ("vertical".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O(multiple));   // 纵向放大
            }else if ("all".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O_H(multiple)); // 放大字体
            }
        }
        printer.sendByte(printer.printTwoData(leftText, rightText).getBytes(Charset.forName("GBK")));  // 设置打印内容
        if (isEnter) {
            printer.sendByte(commands.POS_S_PrintLine(enterLine));  // 换行
        }
    }

    /**
     * 设置打印内容:一行有3个打印内容
     * @param leftText 左侧打印的文本
     * @param centerText 中间打印的文本
     * @param rightText 右侧打印的文本
     * @param align 对齐模式       0#左对齐, 1#居中, 2#右对齐
     * @param isBold 是否加粗
     * @param isEnlarge 是否放大
     * @param direction 放大方向    horizontal#水平放大, vertical#垂直放大, all#整体放大
     * @param multiple 放大倍数     [0,7]
     * @param isEnter 是否回车
     * @param enterLine 换行数     [1.10]
     */
    private void printThreeData(String leftText, String centerText, String rightText, int align, boolean isBold, boolean isEnlarge, String direction, int multiple, boolean isEnter, int enterLine){

        printer.sendByte(commands.POS_Set_PrtInit());   // 初始化格式
        printer.sendByte(commands.POS_S_Align(align));  // 对齐模式
        if (isBold){
            printer.sendByte(commands.Set_Bold());      //加粗
        }
        if (isEnlarge){
            if ("horizontal".equals(direction)){
                printer.sendByte(commands.POS_S_GS_H(multiple));   // 横向放大
            }else if ("vertical".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O(multiple));   // 纵向放大
            }else if ("all".equals(direction)){
                printer.sendByte(commands.POS_S_GS_O_H(multiple)); // 放大字体
            }
        }
        printer.sendByte(printer.printThreeData(leftText, centerText, rightText).getBytes(Charset.forName("GBK")));  // 设置打印内容
        if (isEnter) {
            printer.sendByte(commands.POS_S_PrintLine(enterLine));  // 换行
        }
    }

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容