#Android-简单应用编写

😘5ea1
前面已经有一篇博客写安卓初探了,结构什么的也没什么好说的,直接开造
EzLog
创建项目的时候选择空项目,然后语言选择java
布局文件
跟qt一样,AndroidStudio中的布局文件也可以使用可视化的方式构建,首先在res资源文件夹下创建/layout/activity_main.xml作为布局文件,打开后点击右上角切换图片设计视图
这样就可以从组件盒中拿取组件进行拼接了
注意,此处要使用androidx.constraintlayout.widget.ConstraintLayout
进行构建,如果使用另外两个,组件可能会堆积在左上角或某一条边上
因为要实现一个简单的登录应用,所以这里我只使用了两个EditView输入框和一个Button按钮,其中有一个输入框用了密码模板,输入的时候自动变成黑点,且做了输入hint。
为了确保组件位置,还需要对组件进行定位,此处可以使用手动定位,也可以使用上方便捷工具进行推测定位
主程序
完成了布局的定义,此时可以直接编写主程序调用这几个组件了
button = findViewById(R.id.button);
editTextText = findViewById(R.id.editTextText);
editTextTextPassword = findViewById(R.id.editTextTextPassword);
此处通过id的方式寻找所要的组件
因为登录操作需要按下按钮,所以此处设置一个侦听事件来检查按钮有没有被按下,若按下了,则执行内部函数
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v)
{
String name = editTextText.getText().toString();
String password = editTextTextPassword.getText().toString();
String correctUsername = getString(R.string.correct_username);
String correctPassword = getString(R.string.correct_password);
if (name.equals(correctUsername) && password.equals(correctPassword))
{
showRightDialog(MainActivity.this);
}
else
{
showRetryDialog(MainActivity.this);
}
}
});
这里使用了getText()
函数从string.xml资源文件中读取账号和密码,再使用.equal
函数来进行比较,若正确,则执行函数showRightDialog()
;反之则执行showRetryDialog()
private void showRetryDialog(Context context)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("验证失败")
.setMessage("再试试")
.setPositiveButton("确定", null)
.show();
}
private void showRightDialog(Context context)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("正确")
.setMessage("恭喜")
.setPositiveButton("确定", null)
.show();
}
这两个函数就是写在同一文件下的函数
运行
运行后得到一个朴素的主界面,可以输入账号密码尝试登录
#Calc
写一个简单而单纯的计算器
布局
这个应用最繁琐的步骤就是设计这个布局文件,计算器嘛,键肯定少不了,要是限制定死了,不一样的设备间距一变,肯定就死了。

关键点:
- 最左侧键按照最左侧边定位,中间的左右定位,最右侧不要和左侧定位
- 设置text框的时候要注意是Edit的还是Text,和后续读取有关
主程序
只需要设置大量的按钮触发事件,代码就和一个普通控制台计算器一样了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
//老样子,先把所有的组件打印出来
TextView TextviewIn = findViewById(R.id.textViewInput);
TextView TextviewOut = findViewById(R.id.textViewOutput);
Button button0 = findViewById(R.id.buttonNumber0);
Button button1 = findViewById(R.id.buttonNumber1);
Button button2 = findViewById(R.id.buttonNumber2);
Button button3 = findViewById(R.id.buttonNumber3);
Button button4 = findViewById(R.id.buttonNumber4);
Button button5 = findViewById(R.id.buttonNumber5);
Button button6 = findViewById(R.id.buttonNumber6);
Button button7 = findViewById(R.id.buttonNumber7);
Button button8 = findViewById(R.id.buttonNumber8);
Button button9 = findViewById(R.id.buttonNumber9);
Button buttonand = findViewById(R.id.buttonAnd);
Button buttoninc = findViewById(R.id.buttonInc);
Button buttondiv = findViewById(R.id.buttonDiv);
Button buttonchen = findViewById(R.id.buttonChen);
Button buttonxor = findViewById(R.id.buttonXor);
Button buttondot = findViewById(R.id.buttonDot);
Button buttoncalc = findViewById(R.id.buttonCalc);
Button buttonc = findViewById(R.id.buttonC);
ButtonClickListener clickListener = new ButtonClickListener(this, TextviewIn, TextviewOut);
button0.setOnClickListener(clickListener);
button1.setOnClickListener(clickListener);
button2.setOnClickListener(clickListener);
button3.setOnClickListener(clickListener);
button4.setOnClickListener(clickListener);
button5.setOnClickListener(clickListener);
button6.setOnClickListener(clickListener);
button7.setOnClickListener(clickListener);
button8.setOnClickListener(clickListener);
button9.setOnClickListener(clickListener);
buttonand.setOnClickListener(clickListener);
buttoninc.setOnClickListener(clickListener);
buttonchen.setOnClickListener(clickListener);
buttondiv.setOnClickListener(clickListener);
buttonxor.setOnClickListener(clickListener);
buttondot.setOnClickListener(clickListener);
buttoncalc.setOnClickListener(clickListener);
buttonc.setOnClickListener(clickListener);
//设置了大量的按钮相关组件
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
这里为每个按钮设置了一个监听,来保证不同的按钮触发不同事件,所以按钮事件的监听器需要重写
ButtonClickListener重写
public class ButtonClickListener implements View.OnClickListener {
private Context context;
private TextView Textview;
private TextView TextViewOutput;
public ButtonClickListener(Context context, TextView Textview,TextView TextViewOutput) {
this.context = context;
this.Textview = Textview;
this.TextViewOutput= TextViewOutput;
}
@Override
public void onClick(View v) {
String currentText = Textview.getText().toString();
if (currentText.equals("0") && v.getId() != R.id.buttonDot && v.getId() != R.id.buttonCalc) {
currentText = "";
}
if (v.getId() == R.id.buttonNumber0) {
if (!currentText.isEmpty()) {
Textview.setText(currentText + "0");
} else {
Textview.setText("0");
}
}
if (v.getId() == R.id.buttonNumber0) {
Textview.setText(currentText + "0");
} else if (v.getId() == R.id.buttonNumber1) {
Textview.setText(currentText + "1");
} else if (v.getId() == R.id.buttonNumber2) {
Textview.setText(currentText + "2");
} else if (v.getId() == R.id.buttonNumber3) {
Textview.setText(currentText + "3");
} else if (v.getId() == R.id.buttonNumber4) {
Textview.setText(currentText + "4");
} else if (v.getId() == R.id.buttonNumber5) {
Textview.setText(currentText + "5");
} else if (v.getId() == R.id.buttonNumber6) {
Textview.setText(currentText + "6");
} else if (v.getId() == R.id.buttonNumber7) {
Textview.setText(currentText + "7");
} else if (v.getId() == R.id.buttonNumber8) {
Textview.setText(currentText + "8");
} else if (v.getId() == R.id.buttonNumber9) {
Textview.setText(currentText + "9");
} else if (v.getId() == R.id.buttonAnd) {
Textview.setText(currentText + "+");
} else if (v.getId() == R.id.buttonInc) {
Textview.setText(currentText + "-");
} else if (v.getId() == R.id.buttonDiv) {
Textview.setText(currentText + "/");
} else if (v.getId() == R.id.buttonChen) {
Textview.setText(currentText + "*");
} else if (v.getId() == R.id.buttonXor) {
Textview.setText(currentText + "^");
} else if (v.getId() == R.id.buttonC) {
Textview.setText("0");
TextViewOutput.setText("");
} else if (v.getId() == R.id.buttonDot) {
if (currentText.isEmpty() || currentText.charAt(currentText.length() - 1) != '.') {
Textview.setText(currentText + ".");
}
} else if (v.getId() == R.id.buttonCalc) {
if (isValidExpression(currentText)) {
try{
Toast.makeText(Textview.getContext(), "计算", Toast.LENGTH_SHORT).show();
double result = CalculatorUtils.calculateExpression(currentText);
TextViewOutput.setText(String.valueOf(result));
} catch (Exception e) {
Textview.setText("0");
Toast.makeText(Textview.getContext(), "计算错误", Toast.LENGTH_SHORT).show();
}
} else {
Textview.setText("0");
TextViewOutput.setText("");
Toast.makeText(Textview.getContext(), "输入不合法", Toast.LENGTH_SHORT).show();
}
}
}
private boolean isValidExpression(String expression) {
if (expression.isEmpty()) {
return false;
}
char lastChar = expression.charAt(expression.length() - 1);
if (lastChar == '+' || lastChar == '-' || lastChar == '*' || lastChar == '/' || lastChar == '^') {
return false;
}
// 新增:检查连续运算符
for (int i = 0; i < expression.length() - 1; i++) {
char currentChar = expression.charAt(i);
char nextChar = expression.charAt(i + 1);
if ((currentChar == '+' || currentChar == '-' || currentChar == '*' || currentChar == '/' || currentChar == '^') &&
(nextChar == '+' || nextChar == '-' || nextChar == '*' || nextChar == '/' || nextChar == '^')) {
return false;
}
}
int dotCount = 0;
for (int i = 0; i < expression.length(); i++) {
if (expression.charAt(i) == '.') {
boolean hasDigitBeforeDot = false;
boolean hasDigitAfterDot = false;
for (int j = i - 1; j >= 0; j--) {
if (Character.isDigit(expression.charAt(j))) {
hasDigitBeforeDot = true;
break;
}
}
for (int j = i + 1; j < expression.length(); j++) {
if (Character.isDigit(expression.charAt(j))) {
hasDigitAfterDot = true;
break;
}
}
if (!hasDigitBeforeDot || (!hasDigitAfterDot && i != expression.length() - 1)) {
return false;
}
}
}
return true;
}
}
这段代码主要实现了两个功能
- 用了大量的if-else语句来为不同的button-id情况分别讨论(实际上,这里可以使用一些更加简练的表达方式)
- 写了一个验证器,来判断输入的数据是否合法,防止不合法的输入传入本地库中
可以看到,在读入calc按钮事件(即计算事件)的时候,代码中调用了一个函数来计算
CalculatorUtils
调用的函数属于CalculatorUtils类中,而这个类的代码很简单,就是访问本地库,从库中拿代码
package com.example.calc;
public class CalculatorUtils {
static {
System.loadLibrary("calc");
}
public static native double calculateExpression(String expression);
}
calc.cpp
即平常说的.so文件,c++本地库
#include <jni.h>
#include <stack>
#include <string>
#include <cmath>
// 定义运算符优先级
int precedence(char op) {
if (op == '+' || op == '-')
return 1;
if (op == '*' || op == '/')
return 2;
if (op == '^')
return 3;
return 0;
}
// 应用运算符
double applyOp(double a, double b, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
case '^': return std::pow(a, b);
}
return 0;
}
// 计算表达式
extern "C" JNIEXPORT jdouble JNICALL
Java_com_example_calc_CalculatorUtils_calculateExpression(JNIEnv *env, jobject /* this */, jstring expressionStr) {
const char *expression = env->GetStringUTFChars(expressionStr, nullptr);
std::stack<double> values;
std::stack<char> ops;
for (int i = 0; expression[i] != '\0'; i++) {
if (expression[i] == ' ')
continue;
else if (isdigit(expression[i]) || expression[i] == '.') {
std::string valStr;
while (i < strlen(expression) && (isdigit(expression[i]) || expression[i] == '.')) {
valStr += expression[i++];
}
i--;
values.push(std::stod(valStr));
} else if (expression[i] == '(') {
ops.push(expression[i]);
} else if (expression[i] == ')') {
while (!ops.empty() && ops.top() != '(') {
double val2 = values.top();
values.pop();
double val1 = values.top();
values.pop();
char op = ops.top();
ops.pop();
values.push(applyOp(val1, val2, op));
}
if (!ops.empty())
ops.pop();
} else {
while (!ops.empty() && precedence(ops.top()) >= precedence(expression[i])) {
double val2 = values.top();
values.pop();
double val1 = values.top();
values.pop();
char op = ops.top();
ops.pop();
values.push(applyOp(val1, val2, op));
}
ops.push(expression[i]);
}
}
while (!ops.empty()) {
double val2 = values.top();
values.pop();
double val1 = values.top();
values.pop();
char op = ops.top();
ops.pop();
values.push(applyOp(val1, val2, op));
}
env->ReleaseStringUTFChars(expressionStr, expression);
return values.top();
}
实现了计算器的计算式分割和计算功能
测试
EzCalc
写一个简单的计算器,但真的只是计算器吗
项目考虑使用上面的计算器源码,加一个Crypto模块
构筑
在源代码中,在应用层获取到的数据会以字符串的形式传输到本地层so库中,可以考虑在此处加一个侦听模块,当侦听到特定的输入的时候,开放解密模块
解密模块采取某种解密形式,将用户输入要计算的字符串进行解密,解密后不进行输出,直接加到缓存里面,逆向者可以从侦听应用的缓存,来获取解密得到的flag