基础基础热敏打印功能

This commit is contained in:
fengge 2026-04-21 00:21:12 +08:00
parent daf6cba15e
commit 3bdf2b9849
13 changed files with 368 additions and 1 deletions

View File

@ -45,6 +45,7 @@ android {
} }
dependencies { dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation("com.jakewharton.timber:timber:5.0.1") implementation("com.jakewharton.timber:timber:5.0.1")
implementation("io.github.justson:agentweb-core:v5.1.1-androidx") implementation("io.github.justson:agentweb-core:v5.1.1-androidx")
implementation("com.github.Justson:Downloader:v5.0.4-androidx") implementation("com.github.Justson:Downloader:v5.0.4-androidx")

Binary file not shown.

View File

@ -47,6 +47,12 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.StandAPP" /> android:theme="@style/Theme.StandAPP" />
<activity
android:name=".printer.PrintTestActivity"
android:exported="false"
android:label="打印测试"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar" />
<activity <activity
android:name="com.example.kingway.ptt.pttActivity" android:name="com.example.kingway.ptt.pttActivity"
android:exported="false" android:exported="false"

BIN
app/src/main/assets/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
app/src/main/assets/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@ -24,6 +24,7 @@ class LoginActivity : AppCompatActivity() {
val cbRemember = findViewById<CheckBox>(R.id.cb_remember) val cbRemember = findViewById<CheckBox>(R.id.cb_remember)
val btnLogin = findViewById<Button>(R.id.btn_login) val btnLogin = findViewById<Button>(R.id.btn_login)
val btnPttExample = findViewById<Button>(R.id.btn_ptt_example) val btnPttExample = findViewById<Button>(R.id.btn_ptt_example)
val btnPrinterTest = findViewById<Button>(R.id.btn_printer_test)
val ivSettings = findViewById<ImageView>(R.id.iv_settings) val ivSettings = findViewById<ImageView>(R.id.iv_settings)
// 1. 加载保存的状态和凭据 // 1. 加载保存的状态和凭据
@ -59,7 +60,17 @@ class LoginActivity : AppCompatActivity() {
val intent = Intent(this, com.example.kingway.ptt.pttActivity::class.java) val intent = Intent(this, com.example.kingway.ptt.pttActivity::class.java)
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
Toast.makeText(this, "打开 PTT 示例失败: ${e.message}", Toast.LENGTH_SHORT).show() Toast.makeText(this, "打开 PTT 示例失败: ${e.message}", Toast.LENGTH_SHORT).show();
}
}
// 5. 打印测试按钮点击事件
btnPrinterTest.setOnClickListener {
try {
val intent = Intent(this, com.stand.standapp.printer.PrintTestActivity::class.java)
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this, "打开打印测试失败: ${e.message}", Toast.LENGTH_SHORT).show()
} }
} }
} }

View File

@ -0,0 +1,218 @@
package com.stand.standapp.printer;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.CompoundButton;
import androidx.appcompat.app.AppCompatActivity;
import com.dp.dp_serialportlist.Serialport_Factory;
import com.stand.standapp.R;
import java.io.IOException;
import java.io.InputStream;
public class PrintTestActivity extends AppCompatActivity implements View.OnClickListener {
static {
try {
System.loadLibrary("szzkserialport");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}
private Spinner spPortPath, spBaudrate;
private EditText etManualPath;
private CheckBox cbManualMode;
private Button btnOpen;
private TextView tvStatus;
private Serialport_Factory sf;
private String[] portNames;
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == Serialport_Factory.PAPER_STATE) {
boolean hasPaper = msg.getData().getBoolean("PAPER_STATE");
tvStatus.setText("打印机状态: " + (hasPaper ? "有纸" : "缺纸"));
Toast.makeText(PrintTestActivity.this, hasPaper ? "打印机有纸" : "打印机缺纸", Toast.LENGTH_SHORT).show();
}
return true;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_printer_test);
initViews();
sf = Serialport_Factory.getSerialport_Factory(this, mHandler);
loadSerialPorts();
}
private void initViews() {
spPortPath = findViewById(R.id.sp_port_path);
spBaudrate = findViewById(R.id.sp_baudrate);
etManualPath = findViewById(R.id.et_manual_path);
cbManualMode = findViewById(R.id.cb_manual_mode);
btnOpen = findViewById(R.id.btn_open);
tvStatus = findViewById(R.id.tv_status);
cbManualMode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
spPortPath.setVisibility(isChecked ? View.GONE : View.VISIBLE);
etManualPath.setVisibility(isChecked ? View.VISIBLE : View.GONE);
}
});
btnOpen.setOnClickListener(this);
findViewById(R.id.btn_print_text).setOnClickListener(this);
findViewById(R.id.btn_print_barcode).setOnClickListener(this);
findViewById(R.id.btn_print_qr).setOnClickListener(this);
findViewById(R.id.btn_print_img).setOnClickListener(this);
findViewById(R.id.btn_print_column).setOnClickListener(this);
findViewById(R.id.btn_print_demo1).setOnClickListener(this);
findViewById(R.id.btn_print_demo2).setOnClickListener(this);
findViewById(R.id.btn_print_test_page).setOnClickListener(this);
findViewById(R.id.btn_check_paper).setOnClickListener(this);
findViewById(R.id.btn_cut_all).setOnClickListener(this);
findViewById(R.id.btn_back).setOnClickListener(this);
}
private void loadSerialPorts() {
try {
portNames = sf.getAllDevices();
if (portNames != null && portNames.length > 0) {
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, portNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spPortPath.setAdapter(adapter);
} else {
Toast.makeText(this, "未检测到串口设备,请检查硬件权限", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_open) {
openPort();
} else if (id == R.id.btn_back) {
finish();
} else {
if (!sf.isConnection()) {
Toast.makeText(this, "请先开启串口", Toast.LENGTH_SHORT).show();
return;
}
handlePrintAction(id);
}
}
private void openPort() {
String path;
if (cbManualMode.isChecked()) {
path = etManualPath.getText().toString().trim();
} else {
if (spPortPath.getSelectedItem() == null) return;
path = spPortPath.getSelectedItem().toString();
}
String baudrate = spBaudrate.getSelectedItem().toString();
if (sf.OpenPort(path, baudrate)) {
tvStatus.setText("状态: 已连接 (" + path + ")");
tvStatus.setTextColor(0xFF4CAF50);
Toast.makeText(this, "串口已开启", Toast.LENGTH_SHORT).show();
} else {
tvStatus.setText("状态: 连接失败");
tvStatus.setTextColor(0xFFFF0000);
Toast.makeText(this, "串口开启失败", Toast.LENGTH_SHORT).show();
}
}
private void handlePrintAction(int id) {
if (id == R.id.btn_print_text) {
sf.PrintText("StandAPP 打印测试", 2, 1, 30);
sf.PrintText("文本内容居左测试", 1, 1, 20);
sf.PaperCut();
} else if (id == R.id.btn_print_barcode) {
sf.PrintBarcode("88888888", 3, 100, 5, 2, 2);
sf.PrintText("\n", 1, 1, 50);
sf.PaperCut();
} else if (id == R.id.btn_print_qr) {
sf.PrintQR("StandAPP-Integrated", 6, 2);
sf.PrintText("\n", 1, 1, 50);
sf.PaperCut();
} else if (id == R.id.btn_print_img) {
Bitmap bm = getImageFromAssetsFile("1.jpg");
if (bm != null) {
sf.PrintImage(bm, 384, 0, 2);
sf.PrintText("\n", 1, 1, 50);
sf.PaperCut();
}
} else if (id == R.id.btn_print_column) {
sf.Printcolumncontent("项目", 0, "单价", 180, "数量", 250, "金额", 320);
sf.Printcolumncontent("测试商品", 0, "10.0", 180, "1", 250, "10.0", 320, 70);
sf.PrintText("\n", 1, 1, 50);
sf.PaperCut();
} else if (id == R.id.btn_print_demo1) {
printDemo(false);
} else if (id == R.id.btn_print_demo2) {
printDemo(true);
} else if (id == R.id.btn_print_test_page) {
sf.PrintTestPage();
} else if (id == R.id.btn_check_paper) {
sf.Check_Paper();
} else if (id == R.id.btn_cut_all) {
sf.PaperAllCut();
}
}
private void printDemo(boolean is3Inch) {
int width = is3Inch ? 400 : 300;
int right = is3Inch ? 120 : 100;
sf.PrintText("\nStandAPP 业务测试单", 2, 1, 30);
sf.PrintText("--------------------------------", 1, 1, 10);
sf.Printcolumncontent("名称", 0, "单价", 180, "数量", 250, "金额", width);
sf.Printcolumncontent("演示商品A", 0, "9.9", 180, "1", 250, "9.90", width, right);
sf.PrintText("--------------------------------", 1, 1, 10);
sf.PrintQR("http://www.standapp.com", 6, 2);
sf.PrintText("\n\n\n", 1, 1, 10);
sf.PaperCut();
}
private Bitmap getImageFromAssetsFile(String fileName) {
try {
InputStream is = getAssets().open(fileName);
Bitmap bitmap = BitmapFactory.decodeStream(is);
is.close();
return bitmap;
} catch (IOException e) {
return null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (sf != null) sf.Disconnect();
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -133,11 +133,21 @@
android:id="@+id/btn_ptt_example" android:id="@+id/btn_ptt_example"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_marginBottom="10dp"
android:background="@drawable/shape_button_login" android:background="@drawable/shape_button_login"
android:text="PTT 示例" android:text="PTT 示例"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="16sp" /> android:textSize="16sp" />
<Button
android:id="@+id/btn_printer_test"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/shape_button_login"
android:text="打印测试"
android:textColor="#FFFFFF"
android:textSize="16sp" />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="返回" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="串口打印集成测试"
android:textSize="18sp"
android:textStyle="bold" />
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1. 串口配置"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="#FFFFFF"
android:orientation="vertical"
android:padding="8dp">
<Spinner
android:id="@+id/sp_port_path"
android:layout_width="match_parent"
android:layout_height="48dp" />
<EditText
android:id="@+id/et_manual_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="手动路径 (如 /dev/ttyS0)"
android:visibility="gone" />
<CheckBox
android:id="@+id/cb_manual_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手动输入模式" />
<Spinner
android:id="@+id/sp_baudrate"
android:layout_width="match_parent"
android:layout_height="48dp"
android:entries="@array/baudrates" />
<Button
android:id="@+id/btn_open"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="打开串口" />
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="4dp"
android:text="状态: 未连接"
android:textColor="#FF0000" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="2. 打印操作"
android:textStyle="bold" />
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:columnCount="2">
<Button android:id="@+id/btn_print_text" android:text="打印文本" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_barcode" android:text="打印条码" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_qr" android:text="打印二维码" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_img" android:text="打印图片" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_column" android:text="多列排版" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_demo1" android:text="2寸样单" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_demo2" android:text="3寸样单" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_print_test_page" android:text="打印自检" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_check_paper" android:text="缺纸检测" android:layout_columnWeight="1"/>
<Button android:id="@+id/btn_cut_all" android:text="全切纸" android:layout_columnWeight="1"/>
</GridLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,9 @@
<resources>
<string-array name="baudrates">
<item>9600</item>
<item>19200</item>
<item>38400</item>
<item>57600</item>
<item>115200</item>
</string-array>
</resources>