水印相机需求:可以选择地点,时间要实时显示,有公司logo等,支持拍照完成支持预览,可以保存到本地功能。
-
附上Demo下载链接:https://download.csdn.net/download/qiushuiduren/90037747 主要功能如下: * 自定义相机,支持水印功能:可直接将layout中的view转化为 bitmap添加到图片上 * 支持前后摄像头切换,自动对焦 * 地址可点击进行跳转选择(可按自己需求更改实现) * 时间为当前时间,动态显示 * 解决相机预览与保存图片位置不一致的问题 * 拍照完成进行图片预览,支持图片放大预览 * 6.0 以上动态申请运行权限 * 支持 ViewBinding绑定控件
先上图片,看看效果图,第三张是保存的图片
网上找了很多例子,没有特别符合的,于是重新完善写了一下
下面直接上代码
布局文件如下,包含水印相机和水印的 view
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5"
android:paddingTop="0dp">
<View
android:id="@+id/view_top"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/black"
app:layout_constraintBottom_toTopOf="@+id/sfv_camera"
app:layout_constraintTop_toTopOf="parent" />
<!-- 设置预览窗口的宽高比为 3:4-->
<com.fix.watercamera.view.ResizeAbleSurfaceView
android:id="@+id/sfv_camera"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/view_bottom_navigation"
app:layout_constraintDimensionRatio="3:4"
app:layout_constraintTop_toBottomOf="@+id/view_top" />
<com.fix.watercamera.view.ZoomImageView
android:id="@+id/iv_picture_preview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/black"
android:scaleType="centerInside"
app:layout_constraintDimensionRatio="3:4"
android:visibility="invisible"
app:layout_constraintTop_toBottomOf="@+id/view_top" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/csl_water_info_container"
android:layout_width="160dp"
android:layout_height="128dp"
android:layout_marginStart="26dp"
android:layout_marginBottom="26dp"
android:alpha="0.9"
android:background="@drawable/shape_background_grey_8"
app:layout_constraintBottom_toBottomOf="@id/sfv_camera"
app:layout_constraintStart_toStartOf="parent">
<View
android:id="@+id/view_water_top_logo"
android:layout_width="0dp"
android:layout_height="34dp"
android:background="@mipmap/icon_water_camera_logo"
app:layout_constraintEnd_toEndOf="@+id/csl_water_info_container"
app:layout_constraintStart_toStartOf="@+id/csl_water_info_container"
app:layout_constraintTop_toTopOf="@+id/csl_water_info_container" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="张三丰(5455454)"
android:textColor="@color/white"
android:textSize="12sp"
app:drawableStartCompat="@mipmap/icon_water_person"
app:layout_constraintBottom_toTopOf="@+id/tv_address"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view_water_top_logo" />
<TextView
android:id="@+id/tv_address"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:drawablePadding="4dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="2"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="水印地址信息"
android:textColor="@color/white"
android:textSize="12sp"
app:drawableStartCompat="@mipmap/icon_water_address"
app:layout_constraintBottom_toTopOf="@+id/tv_time"
app:layout_constraintStart_toStartOf="@id/tv_name"
app:layout_constraintTop_toBottomOf="@id/tv_name" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:drawablePadding="4dp"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:text="2024-10-11 10:56"
android:textColor="@color/white"
android:textSize="12sp"
app:drawableStartCompat="@mipmap/icon_water_time"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/tv_name"
app:layout_constraintTop_toBottomOf="@id/tv_address" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:id="@+id/view_bottom_navigation"
android:layout_width="match_parent"
android:layout_height="170dp"
android:background="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/sfv_camera" />
<ImageView
android:id="@+id/iv_take_pictures"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="30dp"
android:src="@mipmap/icon_take_picture"
app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
app:layout_constraintTop_toTopOf="@id/view_bottom_navigation" />
<ImageView
android:id="@+id/iv_take_pictures_finish"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginTop="30dp"
android:src="@mipmap/icon_take_picture_finish"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
app:layout_constraintTop_toTopOf="@id/view_bottom_navigation" />
<TextView
android:id="@+id/tv_camera_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
android:text="翻转"
android:textColor="@color/white"
android:textSize="10dp"
app:drawableTopCompat="@mipmap/icon_camera_switch"
app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
app:layout_constraintStart_toEndOf="@+id/iv_take_pictures"
app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />
<TextView
android:id="@+id/tv_select_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
android:text="地点"
android:textColor="@color/white"
android:textSize="10dp"
app:drawableTopCompat="@mipmap/icon_camera_address"
app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
app:layout_constraintEnd_toStartOf="@+id/iv_take_pictures"
app:layout_constraintStart_toStartOf="@id/view_bottom_navigation"
app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />
<TextView
android:id="@+id/tv_take_picture_again"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
android:text="重拍"
android:textColor="@color/white"
android:textSize="10dp"
android:visibility="invisible"
app:drawableTopCompat="@mipmap/icon_take_picture_again"
app:layout_constraintBottom_toBottomOf="@+id/iv_take_pictures"
app:layout_constraintEnd_toEndOf="@id/view_bottom_navigation"
app:layout_constraintStart_toEndOf="@+id/iv_take_pictures"
app:layout_constraintTop_toTopOf="@id/iv_take_pictures" />
</androidx.constraintlayout.widget.ConstraintLayout>
下面是水印相机的主代码,代码有点长,注释写的很详细,感兴趣的可以研究下,如有问题还望各位大佬指正
public class WaterCameraActivity extends BaseActivity<ActivityWaterCameraBinding> implements SurfaceHolder.Callback {
private Handler handler;
private Runnable updateTimeTask;
private Camera mCamera;
private int mCameraId;
private Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
private SurfaceHolder mSurfaceHolder;
private Bitmap tempBitmap;
@Override
public void init() {
overridePendingTransition(R.anim.activtiy_alpha_in, R.anim.activtiy_alpha_out);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.sfvCamera.setFocusable(true);
mSurfaceHolder = binding.sfvCamera.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.setKeepScreenOn(true);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.addCallback(this);
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
// 初始化日期及时间
handler = new Handler();
updateTimeTask = new Runnable() {
@Override
public void run() {
// 获取当前时间并格式化为字符串
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
// 更新TextView上的时间
binding.tvTime.setText(currentTime);
// 安排这个任务再次在1000毫秒(1秒)后执行
handler.postDelayed(this, 1000);
}
};
handler.post(updateTimeTask);
// 点击设置对焦
// binding.sfvCamera.setOnTouchListener((v, event) -> {
// mCamera.autoFocus(autoFocusCallback); //设置相机为自动对焦模式,就不用认为去点击了
// return false;
// });
binding.tvCameraSwitch.setOnClickListener(v -> {
switchCamera();
});
// 跳转到选择地址页面
binding.tvAddress.setOnClickListener(v -> {
Toast.makeText(context, "去选择地址", Toast.LENGTH_SHORT).show();
});
binding.tvSelectAddress.setOnClickListener(v -> {
Toast.makeText(context, "去选择地址", Toast.LENGTH_SHORT).show();
});
// 拍照
binding.ivTakePictures.setOnClickListener(v -> {
takePictures();
});
// 删除并重新拍照
binding.tvTakePictureAgain.setOnClickListener(v -> {
tempBitmap = null;
setViewVisibilityOrNot(true);
});
// 保存拍照
binding.ivTakePicturesFinish.setOnClickListener(v -> {
saveBitmapToAlbum(tempBitmap);
// 保存完照片关闭相机
finish();
});
}
/**
* 这是点击surfaceview聚焦所调用的方法
*/
private Camera.AutoFocusCallback autoFocusCallback = (success, camera) -> {
//success = true,聚焦成功,否则聚焦失败
//在这里我们可以在点击相机后是否聚焦成功,然后做我们的一些操作
};
/**
* 检查设备是否有摄像头
*
* @param context context
* @return boolean
*/
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
return true;
}
return false;
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
startCamera(mCameraId);
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
releaseCamera();
}
/**
* 拍照时设置相机控件的显示隐藏
*/
private void setViewVisibilityOrNot(boolean isTakingPicture) {
if (isTakingPicture) {
binding.sfvCamera.setVisibility(View.VISIBLE);
binding.cslWaterInfoContainer.setVisibility(View.VISIBLE);
binding.ivPicturePreview.setVisibility(View.INVISIBLE);
binding.tvTakePictureAgain.setVisibility(View.INVISIBLE);
binding.ivTakePicturesFinish.setVisibility(View.INVISIBLE);
binding.ivTakePictures.setVisibility(View.VISIBLE);
binding.tvCameraSwitch.setVisibility(View.VISIBLE);
} else {
binding.ivPicturePreview.setVisibility(View.VISIBLE);
binding.sfvCamera.setVisibility(View.INVISIBLE);
binding.cslWaterInfoContainer.setVisibility(View.INVISIBLE);
binding.tvTakePictureAgain.setVisibility(View.VISIBLE);
binding.ivTakePicturesFinish.setVisibility(View.VISIBLE);
binding.ivTakePictures.setVisibility(View.INVISIBLE);
binding.tvCameraSwitch.setVisibility(View.INVISIBLE);
}
}
/**
* 打开相机
*/
private void startCamera(int mCameraId) {
try {
mCamera = Camera.open(mCameraId);
Camera.getCameraInfo(0, cameraInfo);
Camera.Parameters parameters = mCamera.getParameters();
// 设置图片格式
parameters.setPictureFormat(ImageFormat.JPEG);
// 设置照片质量
parameters.setJpegQuality(100);
// 首先获取系统设备支持的所有颜色特效,如果设备不支持颜色特性将返回一个null, 如果有符合我们的则设置
List<String> colorEffects = parameters.getSupportedColorEffects();
for (String currColor : colorEffects) {
if (currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
parameters.setColorEffect(Camera.Parameters.EFFECT_AQUA);
break;
}
}
// 获取对焦模式
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
// 设置自动对焦
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
// 设置闪光灯自动开启
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
// 自动闪光
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
}
mCamera.setDisplayOrientation(setCameraDisplayOrientation());
// 设置显示
mCamera.setPreviewDisplay(mSurfaceHolder);
// 看需求是否需要指定拍照图片的大小(下面代码为高品质图片)
// List<Camera.Size> photoSizes = parameters.getSupportedPictureSizes();//获取系统可支持的图片尺寸
// int width = 0, height = 0;
// for (Camera.Size size : photoSizes) {
// if (size.width > width) width = size.width;
// if (size.height > height) height = size.height;
// }
// parameters.setPictureSize(width, height);
// 获取相机的支持的预览尺寸,找出相近的,解决 SurfaceView预览和保存的图片大小不一致的问题
Camera.Size closelyPreSize = getCloselyPreSize(binding.sfvCamera.getWidth(), binding.sfvCamera.getHeight(), mCamera.getParameters().getSupportedPreviewSizes());
parameters.setPreviewSize(closelyPreSize.width,closelyPreSize.height);
// 重新设置 SurfaceView的宽高(注:相机的宽高比和layout的是反着的) match_parent=-1
binding.sfvCamera.resize(-1, binding.sfvCamera.getWidth() * closelyPreSize.width /closelyPreSize.height);
// 设置预览图片的尺寸大小
ViewGroup.LayoutParams layoutParams = binding.ivPicturePreview.getLayoutParams();
layoutParams.height =binding.ivPicturePreview.getWidth() * closelyPreSize.width /closelyPreSize.height;
binding.ivPicturePreview.setLayoutParams(layoutParams);
// 设置完成需要再次调用setParameter方法才能生效
mCamera.setParameters(parameters);
// 开始预览
mCamera.startPreview();
} catch (Exception e) {
e.printStackTrace();
releaseCamera();
}
}
/**
* 取接近比例的方法
*/
protected Camera.Size getCloselyPreSize(int surfaceWidth, int surfaceHeight,
List<Camera.Size> preSizeList) {
int ReqTmpWidth;
int ReqTmpHeight;
// 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
if (true) {
ReqTmpWidth = surfaceHeight;
ReqTmpHeight = surfaceWidth;
} else {
ReqTmpWidth = surfaceWidth;
ReqTmpHeight = surfaceHeight;
}
//先查找preview中是否存在与surfaceview相同宽高的尺寸
for (Camera.Size size : preSizeList) {
if ((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)) {
return size;
}
}
// 得到与传入的宽高比最接近的size
float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight;
float curRatio, deltaRatio;
float deltaRatioMin = Float.MAX_VALUE;
Camera.Size retSize = null;
for (Camera.Size size : preSizeList) {
curRatio = ((float) size.width) / size.height;
deltaRatio = Math.abs(reqRatio - curRatio);
if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio;
retSize = size;
}
}
return retSize;
}
/**
* 摄像头转换
*/
private void switchCamera() {
// 切换前后摄像头
if (Camera.getNumberOfCameras() > 1) {
mCameraId = (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) ?
Camera.CameraInfo.CAMERA_FACING_FRONT :
Camera.CameraInfo.CAMERA_FACING_BACK;
if (mCamera != null) {
this.surfaceDestroyed(mSurfaceHolder);
this.surfaceCreated(mSurfaceHolder);
}
}
}
/**
* 拍照逻辑
*/
private void takePictures() {
mCamera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if (tempBitmap != null) {
Matrix m = new Matrix();
// 分别设置前后摄像头保存图片的旋转角度
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
// 后置摄像头
m.postRotate(90);
} else if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
// 前置摄像头
m.postRotate(270);
// 解决镜像问题
m.postScale(-1, 1);
}
tempBitmap = Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), m, true);
// 获取水印bitmap view
Bitmap waterInfoBitmap = createBitmapByView(binding.cslWaterInfoContainer);
// 给照片添加水印
tempBitmap = addWaterInfo(tempBitmap, waterInfoBitmap);
ImageLoadUtil.loadBitmapImage(context, binding.ivPicturePreview, tempBitmap);
setViewVisibilityOrNot(false);
} else {
releaseCamera();
}
}
});
}
/**
* 释放相机资源
*/
private void releaseCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
@Override
protected void onDestroy() {
tempBitmap = null;
// 取消任务以防止内存泄漏
handler.removeCallbacks(updateTimeTask);
super.onDestroy();
}
/**
* 添加水印
*/
public Bitmap addWaterInfo(Bitmap srcBitmap, Bitmap waterBitmap) {
// 获取原始图片与水印图片的宽与高
int srcBitmapWidth = srcBitmap.getWidth();
int srcBitmapHeight = srcBitmap.getHeight();
Bitmap newBitmap = Bitmap.createBitmap(srcBitmapWidth, srcBitmapHeight, Bitmap.Config.ARGB_8888);
Canvas mCanvas = new Canvas(newBitmap);
// 往位图中开始画入src原始图片
mCanvas.drawBitmap(srcBitmap, 0, 0, null);
// 获取surface view 和图片view的换算比例
double ratio = 1.0 * srcBitmapWidth / binding.sfvCamera.getWidth();
// 获取水印图片的位置及大小
int left = (int) (binding.cslWaterInfoContainer.getLeft() * ratio);
int right = (int) (binding.cslWaterInfoContainer.getRight() * ratio);
int height = (int) (binding.cslWaterInfoContainer.getHeight() * ratio);
int top = srcBitmapHeight - height - left; // 减去left是为了保证图片的左边距和下边距相同
int bottom = srcBitmapHeight - left;
Paint paint = new Paint();
paint.setAlpha(230); // 设置透明度 0 ~ 255
mCanvas.drawBitmap(waterBitmap, null, new Rect(left, top, right, bottom), paint);
mCanvas.save();
mCanvas.restore();
return newBitmap;
}
/**
* view转bitmap
*
* @param view view
* @return Bitmap
*/
private Bitmap createBitmapByView(View view) {
final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
/**
* 保存bitmap到相册
*/
private void saveBitmapToAlbum(Bitmap bitmap) {
final File appDir = new File(context.getExternalCacheDir(), "image");
if (!appDir.exists()) {
appDir.mkdir();
}
final String fileName = System.currentTimeMillis() + ".jpg";
final File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
try {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
} finally {
fos.close();
}
//把文件插入到系统相册
String tempPath = MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null);
String realPath = getRealPathFromURI(Uri.parse(tempPath));
//通知图库更新
// context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(file.getPath()))));
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse(tempPath)));
// Toast 保存路径
Toast.makeText(context, realPath, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
releaseCamera();
}
public String getRealPathFromURI(Uri contentUri) {//通过本地路经 content://得到URI路径
Cursor cursor = null;
String locationPath = null;
try {
String[] proj = {MediaStore.Images.Media.DATA};
cursor = getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
locationPath = cursor.getString(column_index);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return locationPath;
}
public int setCameraDisplayOrientation() {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(0, info);
int rotation = this.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else {
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
}
加载图片的方法用的是 Glide,很简单,加载一下图片就可以了
ResizeAbleSurfaceView 是自定义可以改变宽高的 SurfaceView,方便动态的改变大小,达到预览效果和图片效果水印位置一致的目的,下面是代码
public class ResizeAbleSurfaceView extends SurfaceView {
private int mWidth = -1;
private int mHeight = -1;
public ResizeAbleSurfaceView(Context context) {
super(context);
}
public ResizeAbleSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeAbleSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (-1 == mWidth || -1 == mHeight) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
else {
setMeasuredDimension(mWidth, mHeight);
}
}
public void resize(int width, int height) {
mWidth = width;
mHeight = height;
getHolder().setFixedSize(width, height);
requestLayout();
invalidate();
}
}
基本就这些,希望能帮到你,感兴趣的同学可以研究下