最近在学习图片根据手势缩放的功能;在网上下载了一个别人写的自定义ImageView;将在使用的过程中遇到的一些问题做个笔记作为经验;
自定义ImageView的gitHub地址为:https://github.com/Gloomyer/ZoomImageView;感谢这位仁兄的分享;
在下载demo中ZoomImageView是直接在代码中创建的:
ZoomImageView ziv = new ZoomImageView(MultiImageAct.this);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
ziv.placeholder(R.mipmap.ic_launcher);
ziv.reSetState();
Glide.with(MultiImageAct.this)
.load(imgs[position])
.into(ziv);
但我是在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"
android:gravity="center_horizontal|center_vertical">
<com.szjx.trighnctcm.custom.ZoomImageView
android:id="@+id/ziv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:src="@drawable/bg_launcher" />
</RelativeLayout>
Activity中获取该自定义ImageView的代码:
View view = LayoutInflater.from(TestZoomImageViewActivity.this).inflate(R.layout.item_testzoomimageview,container,false);
ZoomImageView zoomImageView = (ZoomImageView) view.findViewById(ziv);
zoomImageView.setImageResource(mImgs[position]);
然后运行的时候尽然奔溃报错了:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.szjx.trighnctcm, PID: 9332
android.view.InflateException: Binary XML file line #7: Error inflating class com.szjx.trighnctcm.custom.ZoomImageView
at android.view.LayoutInflater.createView(LayoutInflater.java:633)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at com.szjx.trighnctcm.activity.TestZoomImageViewActivity$1.instantiateItem(TestZoomImageViewActivity.java:58)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:870)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1020)
at android.support.v4.view.ViewPager.populate(ViewPager.java:952)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1474)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2743)
at android.view.View.measure(View.java:17923)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2413)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1415)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1639)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1293)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6599)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5667)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:962)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:288)
at android.view.LayoutInflater.createView(LayoutInflater.java:607)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at com.szjx.trighnctcm.activity.TestZoomImageViewActivity$1.instantiateItem(TestZoomImageViewActivity.java:58)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:870)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1020)
at android.support.v4.view.ViewPager.populate(ViewPager.java:952)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1474)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2743)
at android.view.View.measure(View.java:17923)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2413)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1415)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1639)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1293)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6599)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5667)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:962)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Matrix.reset()' on a null object reference
at com.szjx.trighnctcm.custom.ZoomImageView.reSetState(ZoomImageView.java:298)
at com.szjx.trighnctcm.custom.ZoomImageView.setImageDrawable(ZoomImageView.java:564)
at android.widget.ImageView.<init>(ImageView.java:148)
at android.widget.ImageView.<init>(ImageView.java:135)
at com.szjx.trighnctcm.custom.ZoomImageView.<init>(ZoomImageView.java:0)
at com.szjx.trighnctcm.custom.ZoomImageView.<init>(ZoomImageView.java:0)
at java.lang.reflect.Constructor.newInstance(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:288)
at android.view.LayoutInflater.createView(LayoutInflater.java:607)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at com.szjx.trighnctcm.activity.TestZoomImageViewActivity$1.instantiateItem(TestZoomImageViewActivity.java:58)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:870)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1020)
at android.support.v4.view.ViewPager.populate(ViewPager.java:952)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1474)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1723)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:785)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:654)
at android.view.View.measure(View.java:17923)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5698)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:465)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2743)
at android.view.View.measure(View.java:17923)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2413)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1415)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1639)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1293)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6599)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5667)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:962)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
然后将创建的方法改成和demo一样放在代码中创建,运行就不报错了是OK的;
与同事一起研究了一会之后发现是由于自定义View的方法调用顺序和一些属性初始化位置引起的;
自定义ImageView的构造方法:
public ZoomImageView(Context context) {
this(context, null);
}
public ZoomImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//setScaleType(ScaleType.MATRIX);
mMatrix = new Matrix();
mScaleGestureDetector = new ScaleGestureDetector(context, this);
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
if (isScaleing || getScale() >= mMaxScale)
return true;
isScaleing = true;
float x = e.getX();
float y = e.getY();
if (getScale() < mMidScale) {
postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16);
} else {
postDelayed(new AutoScaleRunnable(mMinScale, x, y), 16);
}
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (onClickListener != null) {
onClickListener.onClick(ZoomImageView.this);
return true;
}
return false;
}
});
setOnTouchListener(this);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
events = new ArrayList<>();
}
同时在自定义ImageView中还重写了几个图片设置的方法:
@Override
public void setImageBitmap(Bitmap bm) {
super.setImageBitmap(bm);
reSetState();
}
@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
reSetState();
}
@Override
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
reSetState();
}
/**
* 设置初始化状态为false
*/
public void reSetState() {
isInit = false;
setTag(null);
mMatrix.reset();
}
在报错的信息里面提示setImageDrawable(Drawable drawable)这个方法调用的reSetState()中的mMatrix.reset();这句代码的报的空指针错误;也就是mMatrix报了空指针错误,但是在构造方法里面明明对他进行初始化了的啊;
而且在代码中初始化这个自定义ImageView不报错,在xml布局文件李面创建就报错;
原来在代码中创的时候是是没有给它设置图片的也就没有在创建的过程中调用setImageDrawable(Drawable drawable)等设置图片的方法;而在xml布局文件中我是给它设置了一个默认图片的,也就是在初始化的过程中调用了setImageDrawable(Drawable drawable)方法;调用的过程是这样的:
读取xml文件执行进入第三个构造方法,首先调用父类的构造方法,在父类的构造方法里面根据xml的内容得到Drawable对象不是为null,所以会调用setImageDrawable(Drawable drawable)方法,但是此时的子类的构造方法里面的代码是还没有执行的,包括 mMatrix = new Matrix();于是在reSetState()中mMatrix就报了空指针错误了;而在代码中创建自定义ImageView时,在父类的构造方法里面得到的Drawable对象为null,所以不会调用setImageDrawable(Drawable drawable)方法,父类构造方法执行完成后调用子类构造方法,在子类的构造方法中初始化了mMatrix,
然后在Activity的代码中给自定义ImageView设置图片时不会报控制针错误;所以在reSetState()进行一个是否为空的判断就可以搞定这个BUG;
/**
* 设置初始化状态为false
*/
public void reSetState() {
isInit = false;
setTag(null);
if(mMatrix != null) {
mMatrix.reset();
}
}