Android学习笔记
配置
屏幕属性设置
屏幕属性设置需要在onCreate方法中地setContext()方法调用前调用。
全屏显示
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
注意MainActivity要继承Activity,如果继承AppCompatActivity无效。
或者在style中进行配置,如此可不用添加代码,同时也可以继承自AppCompatActivity类
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="NoTitleFullScreen" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
</resources>
保持屏幕唤醒
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
//需要在AndroidManifest.xml中添加权限属性
//<uses-permission android:name="android.permission.WAKE_LOCK"/>
横屏设置
- 配置位置:在AndroidManifest.xml文件中的activity标签内
- 配置参数:android:screenOrientation="sensorLandscape"
相机获取图像
CameraManager类
相机设备管理类
名称 | 功能 |
---|---|
getCameraIdList() | 获取相机Id列表 |
openCamera() | 打开相机 |
报错
Access denied finding property "camera.hal1.packagelist"
在Manifest.xml文件中,加入外部存储写入权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
PC传图像到Android
采用Android原生socket接收pc端python发送的图片数据
graph
AA[开始] --> A[PC获取图像长, 宽, 高组合成字符串, 用逗号分隔]
AA --> BB[Android等待接收尺寸数据]
A --> B[PC转化为byte数组]
B --> C[PC发送尺寸信息]
C --> CD[PC等待Android的接收完毕响应信息]
C --> D[Android接收到尺寸信息]
BB --> D
D --> E[Android解析尺寸信息]
E --> F[Android发送接收完毕响应信息]
F --> G[PC端接收到响应信息]
CD --> G
F --> H[Android等待接收图片数据]
G --> I[PC发送图片数据]
I --> II[PC等待Android的接收完毕响应信息]
I --> J[Android接收到图片数据]
H --> J
J --> K[Android发送接收完毕响应信息]
K --> L[Android解析图片数据]
L --> M[Android创建bitmap]
M --> N[Android触发显示控件刷新]
K --> NN[PC端接收到响应信息]
II --> NN
N --> O[结束]
NN --> O
自定义控件
一、定义控件属性
在values文件夹下,在属性配置文件attr.xml中添加控件属性,如文件不存在,则可以创建一个。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SliderBar">
<attr name="BarLength" format="integer"/> <!-- 定义滑杆长度 -->
<attr name="BarWidth" format="integer"/> <!-- 定义滑杆宽度 -->
</declare-styleable>
</resources>
二、创建控件类
该类可以继承不同类型Android原生控件,在本例中,创建了SliderBar控件类,继承View。
public class SliderBar extends View{}
三、定义一些全局变量
private int barLength;
private int barWidth;
private float barRadius;
private Paint barBackgroundPaint;
private Paint blockPaint;
private Point blockPosition;
private Point mCenterPoint;
private SliderBar.OnShakeListener mOnShakeListener;
四、创建构造函数
构造函数中实现对画笔,控件位置等全局变量和attr.xml中定义的属性的初始化。
public SliderBar(Context context, AttributeSet attrs) {
super(context, attrs);
initAttribute(context, attrs);
if (isInEditMode()) {
}
// 移动区域画笔
barBackgroundPaint = new Paint();
barBackgroundPaint.setAntiAlias(true);
// 滑块画笔
blockPaint = new Paint();
blockPaint.setAntiAlias(true);
// 中心点
mCenterPoint = new Point();
// 滑块位置
blockPosition = new Point();
}
private void initAttribute(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SliderBar);
// 摇杆半径
barLength = typedArray.getInteger(R.styleable.SliderBar_BarLength, 420);
barWidth = typedArray.getInteger(R.styleable.SliderBar_BarWidth, 40);
//距离级别
typedArray.recycle();
}
五、重构onMeasure函数
onMeasure函数定义了控件的大小。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth, measureHeight;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
// 具体的值和match_parent
measureWidth = widthSize;
} else {
// wrap_content
measureWidth = barLength+barWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
measureHeight = heightSize;
} else {
measureHeight = barWidth*2+15;
}
setMeasuredDimension(measureWidth, measureHeight);
}
六、重构onDraw函数
onDraw函数为控件绘制函数,用于绘制控件形状等。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
int cx = measuredWidth / 2;
int cy = measuredHeight / 2;
// 中心点
mCenterPoint.set(cx, cy);
// 可移动区域的半径
barRadius = (barLength - barWidth) / 2;
// 摇杆位置
if (0 == blockPosition.x || 0 == blockPosition.y) {
blockPosition.set(mCenterPoint.x, mCenterPoint.y);
}
// 画可移动区域
Rect rect = new Rect();
rect.left = (int) (mCenterPoint.x - barRadius);
rect.top = mCenterPoint.y - barWidth / 2;
rect.right = (int) (mCenterPoint.x + barRadius);
rect.bottom = mCenterPoint.y + barWidth / 2;
barBackgroundPaint.setColor(Color.GRAY);
canvas.drawRect(rect, barBackgroundPaint);
canvas.drawCircle(rect.left, mCenterPoint.y, barWidth/2, barBackgroundPaint);
canvas.drawCircle(rect.right, mCenterPoint.y, barWidth/2, barBackgroundPaint);
blockPaint.setColor(Color.RED);
canvas.drawCircle(blockPosition.x, mCenterPoint.y, barWidth, blockPaint);
}
七、重构onTouchEvent函数
onTouchEvent函数处理控件触控事件的响应。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下
// 回调 开始
callBackStart();
case MotionEvent.ACTION_MOVE: // 移动
float moveX = event.getX();
float moveY = event.getY();
blockPosition = getBlockPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), barRadius);
moveRocker(blockPosition.x, blockPosition.y);
break;
case MotionEvent.ACTION_UP: // 抬起
case MotionEvent.ACTION_CANCEL: // 移出区域
// 回调 结束
callBackFinish();
if (mOnShakeListener != null) {
mOnShakeListener.getData(0.0);
}
moveRocker(mCenterPoint.x, mCenterPoint.y);
break;
}
return true;
}
private Point getBlockPositionPoint(Point centerPoint, Point touchPoint, float radius){
float dis = touchPoint.x - centerPoint.x;
int showPointX = touchPoint.x;
float abs_dis = Math.abs(dis);
if (abs_dis > radius){
showPointX = (int) (centerPoint.x + (dis / abs_dis) * radius);
}
int showPointY = centerPoint.y;
callBack(showPointX - centerPoint.x);
return new Point(showPointX, showPointY);
}
private void moveRocker(int x, int y) {
blockPosition.set(x, y);
invalidate();
}
private void callBackStart() {
if (null != mOnShakeListener) {
mOnShakeListener.onStart();
}
}
private void callBack(double x) {
if (null != mOnShakeListener) {
mOnShakeListener.getData(x / barRadius);
}
}
private void callBackFinish() {
if (null != mOnShakeListener) {
mOnShakeListener.onFinish();
}
}
八、给出监听事件配置函数
public void setOnShakeListener(SliderBar.OnShakeListener listener) {
mOnShakeListener = listener;
}
九、创建回调函数接口
public interface OnShakeListener {
// 开始
void onStart();
/**
* 摇动方向
*
* @param x 坐标
*/
void getData(double x);
// 结束
void onFinish();
}
Android JNI配置Opencv
一、编译Opencv和Opencv contrib源码
step 1. 下载源码
opencv源码: https://github.com/opencv/opencv/releases/tag/4.5.4
opencv_contrib源码: https://github.com/opencv/opencv_contrib/releases/tag/4.5.4
源码下载完成后,两分源码解压到同一个目录下,目录结构如下:
# root
# |- opencv
# └- opencv-contrib
step 2. 编译源码
在根目录下,运行如下指令。
python ./opencv/platforms/android/build_sdk.py \
--extra_modules_path=path_to_opencv_contrib/modules/ \
--config ./opencv/platforms/android/ndk-22.config.py \
--sdk_path=path_to_sdk
⚠️注意:此处运行需要python环境,需要先解决python的环境依赖问题。
step 3. 代码部署
编译完成后,会在根目录下生成OpenCV-android-sdk文件夹,我们需要的文件内容都在里边
将该文件夹复制到Android的jni目录下,配置CMakeLists.txt
配置内容如下:
# 引入头文件
include_directories(${CMAKE_SOURCE_DIR}/OpenCV-android-sdk/sdk/native/jni/include)
# 引入库文件
set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
# 链接opencv
target_link_libraries( # Specifies the target library.
preciselanding
# Links the target library to the log library
# included in the NDK.
${log-lib}
${OpenCV_LIBS}
${jnigraphics-lib})
项目框架
一、MVVM框架
LiveData+ViewModel+DataBinding架构
注意事项: 在Activity中创建的变量会随着屏幕旋转而重置。
LiveData
MutableLiveData<String> msg;
ViewModel
public class testViewModel extends ViewModel
{
;
}
DataBinding
xml
<data>
</data>
在layout中格式化字符串
android:text='@{String.format("%f", vm.var)}'
Activity基本实现
// mainBinding由系统自动生成,类的名字由首字母大写驼峰形式的xml名字+Binding组成
mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 由ViewModelProvider创建vm类
mainActivityViewModel = new ViewModelProvider((ViewModelStoreOwner) this, new ViewModelProvider.NewInstanceFactory()).get(MainActivityViewModel.class);
// 将databinding和vm类进行绑定,其中方法名称由set+xml中定义的变量名,以首字母小写的驼峰形式呈现
mainBinding.setTest(mainActivityViewModel); // 变量test绑定ViewModel
mainBinding.setClick(new ClickClass()); // 变量click绑定ViewModel
mainBinding.setLifecycleOwner(this); //
防止Activity中变量重置
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putInt("count",times);
super.onSaveInstanceState(outState);
}
// 防止旋转数据丢失,在activity中的关键数据在重构后进行恢复
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
times = savedInstanceState.getInt("count");
}