Android自定义View进阶:基于subsampling-scale-image-view扩展功能
subsampling-scale-image-view是一个专为Android平台设计的高级图片查看库,能够高效显示超大图片而不会出现内存溢出问题。该库支持 pinch 缩放、平移、旋转和动画等功能,并且具有高度的可配置性和可扩展性,非常适合构建照片画廊、地图查看器和建筑平面图等应用场景。项目核心代码位于library/src/main/java/com/davemorrissey/labs/subscaleview/目录,官方使用指南可参考README.md。
扩展原理与基础架构
扩展subsampling-scale-image-view的核心在于继承SubsamplingScaleImageView类并重写关键方法。该库的设计遵循开闭原则,允许开发者在不修改原有代码的情况下添加新功能。主要扩展点包括:
- 绘制扩展:通过重写
onDraw()方法添加自定义图形元素 - 事件扩展:通过实现
OnImageEventListener监听图片加载状态变化 - 交互扩展:通过重写
onTouchEvent()添加自定义手势处理
项目提供的示例代码展示了三种典型扩展方式,分别位于sample/src/main/java/com/davemorrissey/labs/subscaleview/test/extension/目录下的ExtensionPinFragment、ExtensionCircleFragment和ExtensionFreehandFragment。
实战案例:实现带标记功能的图片查看器
下面以"在图片上添加可随缩放移动的标记"为例,详细介绍扩展实现过程。
1. 创建自定义View类
首先创建PinView类继承SubsamplingScaleImageView,该类将负责绘制标记图标:
public class PinView extends SubsamplingScaleImageView {
private final Paint paint = new Paint();
private final PointF vPin = new PointF();
private PointF sPin;
private Bitmap pin;
public PinView(Context context) {
this(context, null);
}
public PinView(Context context, AttributeSet attr) {
super(context, attr);
initialise();
}
public void setPin(PointF sPin) {
this.sPin = sPin;
initialise();
invalidate();
}
private void initialise() {
// 初始化标记图标
float density = getResources().getDisplayMetrics().densityDpi;
pin = BitmapFactory.decodeResource(this.getResources(), R.drawable.pushpin_blue);
float w = (density/420f) * pin.getWidth();
float h = (density/420f) * pin.getHeight();
pin = Bitmap.createScaledBitmap(pin, (int)w, (int)h, true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 确保图片加载完成后再绘制标记
if (!isReady()) {
return;
}
paint.setAntiAlias(true);
if (sPin != null && pin != null) {
// 将图片坐标系转换为视图坐标系
sourceToViewCoord(sPin, vPin);
float vX = vPin.x - (pin.getWidth()/2);
float vY = vPin.y - pin.getHeight();
canvas.drawBitmap(pin, vX, vY, paint);
}
}
}
关键方法说明:
sourceToViewCoord():将图片原始坐标系中的点转换为当前视图坐标系中的点,确保标记随图片缩放和平移保持正确位置isReady():检查图片是否加载完成,避免在图片未准备好时绘制标记
2. 在布局文件中使用自定义View
创建扩展标记功能的布局文件sample/src/main/res/layout/extension_pin_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.davemorrissey.labs.subscaleview.test.extension.views.PinView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="16dp"
android:background="@drawable/button_standout_pressed"
android:text="Next"/>
</RelativeLayout>
3. 在Fragment中初始化并使用
创建ExtensionPinFragment类加载图片并设置标记位置:
public class ExtensionPinFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.extension_pin_fragment, container, false);
final ExtensionActivity activity = (ExtensionActivity)getActivity();
if (activity != null) {
rootView.findViewById(R.id.next).setOnClickListener(v -> activity.next());
}
PinView imageView = rootView.findViewById(R.id.imageView);
imageView.setImage(ImageSource.asset("sanmartino.jpg"));
// 设置标记在图片坐标系中的位置
imageView.setPin(new PointF(1602f, 405f));
return rootView;
}
}
上述代码通过ImageSource.asset("sanmartino.jpg")加载 assets 目录下的示例图片sample/assets/sanmartino.jpg,并在坐标(1602, 405)处添加标记。
其他扩展案例
除了标记功能,subsampling-scale-image-view还支持多种扩展方式:
圆形标记扩展
CircleView.java演示了如何在图片上绘制可缩放的圆形标记。核心实现是在onDraw()方法中使用canvas.drawCircle()绘制圆形,并通过sourceToViewCoord()方法将原始坐标转换为视图坐标。
自由手绘扩展
FreehandView.java实现了在图片上自由绘制的功能,通过记录触摸轨迹并在onDraw()中重绘实现。该类使用Path对象存储绘制路径,并通过postInvalidate()触发重绘。
性能优化建议
在扩展subsampling-scale-image-view时,需要注意以下性能问题:
- 避免在onDraw中创建对象:所有Paint、Path等对象应在初始化时创建,避免频繁GC
- 合理使用isReady()检查:确保只在图片加载完成后执行绘制操作
- 控制绘制复杂度:复杂路径和大量标记应考虑使用缓存或简化算法
- 谨慎处理触摸事件:复杂手势识别可能导致性能问题,必要时使用手势识别器
完整的性能优化指南可参考官方文档中的"Advanced configuration"章节。
总结与进阶方向
通过继承SubsamplingScaleImageView并合理利用其提供的坐标转换方法,我们可以轻松扩展出各种高级功能。除了本文介绍的标记和绘制功能外,还可以探索以下进阶方向:
- 3D模型叠加:结合OpenGL在图片上叠加3D模型
- 实时测量工具:利用坐标转换实现图片上的尺寸测量
- AR增强现实:将真实世界物体与图片内容关联
项目提供的示例应用sample/src/main/java/com/davemorrissey/labs/subscaleview/test/包含了更多高级用法,建议开发者下载源码后深入研究。要开始使用该库,可通过Gradle将其添加到项目依赖中,具体方法参见README.md中的"Quick start"部分。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




