我有一个自定义视图对象,它读取数据并使用部分剪切的图像显示数据。该课程还允许使用"指示符"滑动按钮以指示数据何时超过一定量。我的问题是双重的。我经过大量工作和测试后遇到的第一个错误是Java.lang.outOfMemory错误,或者是android中的OOM。我通过获取和回收我的BitmapDrawables中的所有位图来修复它,然后使所有这些drawables为null。大约9次屏幕旋转后错误突然出现,但修复了它。然而,它然后创建了另一个错误,我似乎无法立即修复这两个错误。当我到达主屏幕时,通过按回或其他任何事情发生另一个错误。通过在离开之后返回应用程序,我得到一个位图可绘制错误的空引用,这会导致我的应用程序崩溃。
应用程序在旋转屏幕时正确恢复,我没有内存问题,但现在当我完全离开应用程序并返回时,我得到空引用错误。对于为什么有时候我的对象被实例化而其他时候却没有实例化它并没有任何意义。
这是我的观看代码:
public class indBar extends View {
//Variables
private int view_height;
private int view_width;
private int touchY;
private int buttonX;
private int buttonY;
private int buttonX2;
private int buttonY2;
private int barX;
private int barY;
private int barX2;
private int barY2;
private int indX;
private int indY;
private int indX2;
private int indY2;
private int arrowX;
private int arrowY;
private int arrowX2;
private int arrowY2;
private int mActivePointerId;
private int indPerc;
private int levelPerc;
private int currentLevel;
private int backOffset;
private int oldLevel;
private int oldCurrLevel;
private boolean startup = true;
//Resource Management
private ViewTreeObserver viewTreeObserver;
private Resources res;
private BitmapDrawable colorBar;
private BitmapDrawable emptyBar;
private BitmapDrawable unpressedButton;
private BitmapDrawable pressedButton;
private BitmapDrawable indicator;
private BitmapDrawable arrow;
private BitmapDrawable redButton;
private ClipDrawable colorbarClip;
private Rect buttonBounds;
private Rect indBounds;
private Rect arrowBounds;
private RectF borderRect;
private RectF backRect;
private Paint borderPaint;
private Paint backPaint;
private Timer animate;
private int animateDelay = 1;
private int animateSpeed;
private boolean animateComplete = true;
//Defaults
private int def_background = Color.BLACK;
private int SIZE = 200;
//Customizable
private int set_background;
public indBar(Context context, AttributeSet attrs) {
super(context, attrs);
readAttrs(context, attrs, 0);
init();
}
public indBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
readAttrs(context, attrs, defStyleAttr);
init();
}
public indBar(Context context) {
super(context);
readAttrs(context, null, 0);
init();
}
private void init(){
//System
if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB) setLayerType(View.LAYER_TYPE_SOFTWARE, null);
viewTreeObserver = getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
view_width = getWidth();
view_height = getHeight();
afterLayout();
}
});
}
res = getResources();
animateSpeed = 15;
animate = new Timer();
animate.schedule(new TimerTask() {
@Override
public void run() {
animateBar();
}
}, 0, animateDelay);
borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
borderPaint.setFilterBitmap(true);
borderPaint.setDither(true);
borderPaint.setStyle(Paint.Style.FILL);
borderPaint.setColor(Color.BLACK);
backPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backPaint.setFilterBitmap(true);
backPaint.setDither(true);
backPaint.setStyle(Paint.Style.FILL);
backPaint.setColor(set_background);
}
public void afterLayout(){
//Images - Create resource directory object, use it to set an image to a bitmap
// object using bitmap factory and scale it. Then create a drawable using that bitmap.
// Set the bounds of the new drawable bitmap. Assign it to a clip object for partial rendering
backOffset = 20;
borderRect = new RectF(0, 0, view_width, view_height);
backRect = new RectF(backOffset, backOffset, view_width - backOffset, view_height - backOffset);
//Set bar dims
barX = view_width/6;
barX2 = barX + view_width/3;
barY = (view_width - barX2 + 2) / 2;
barY2 = view_height - barY;
//Full Bar
if(colorBar == null) colorBar = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.colorbar));
colorBar.setBounds(barX, barY, barX2, barY2);
if(colorbarClip == null) colorbarClip = new ClipDrawable(colorBar, Gravity.BOTTOM, ClipDrawable.VERTICAL);
colorbarClip.setBounds(barX, barY, barX2, barY2);
colorbarClip.setLevel(currentLevel);
//Empty Bar
if(emptyBar == null) emptyBar = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.colorempty));
emptyBar.setBounds(barX, barY, barX2, barY2);
//Button
buttonX = barX2 + 2;
buttonX2 = view_width - backOffset;
buttonY = (view_height - barY - (((barY2 - barY)*indPerc)/100)) - ((buttonX2 - buttonX)/2);
buttonY2 = buttonY + (buttonX2 - buttonX);
buttonBounds = new Rect(buttonX, buttonY, buttonX2, buttonY2);
if(unpressedButton == null) unpressedButton = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.unpressedbutton));
unpressedButton.setBounds(buttonBounds);
if(redButton == null) redButton = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.redbutton));
redButton.setBounds(buttonBounds);
if(pressedButton == null) pressedButton = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.pressedbutton));
pressedButton.setBounds(buttonBounds);
pressedButton.setVisible(false, false);
redButton.setVisible(false, false);
//Indicator and arrow
int arrowHeight = view_height/40;
indX = view_width/14;
indX2 = buttonX2/2 + backOffset;
indY = buttonY + (buttonY2 - buttonY)/2;
indY2 = indY + 3;
arrowX = indX;
arrowX2 = buttonX2/6;
arrowY = indY - arrowHeight;
arrowY2 = indY2 + arrowHeight;
indBounds = new Rect(indX, indY, indX2, indY2);
arrowBounds = new Rect(arrowX, arrowY, arrowX2, arrowY2);
if(indicator == null) indicator = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.indicator));
if(arrow == null) arrow = new BitmapDrawable(res, BitmapFactory.decodeResource(res, R.drawable.arrow));
indicator.setBounds(indBounds);
arrow.setBounds(arrowBounds);
setLevel(currentLevel);
setIndPerc();
startup = false;
}
@Override
protected void onDraw(Canvas canvas){
if(!startup) {
if(levelPerc >= indPerc) {
redButton.setVisible(true, true);
} else {
redButton.setVisible(false, false);
}
canvas.drawRoundRect(borderRect, 40, 40, borderPaint);
canvas.drawRoundRect(backRect, 40, 40, backPaint);
emptyBar.draw(canvas);
colorbarClip.draw(canvas);
indicator.draw(canvas);
arrow.draw(canvas);
if (unpressedButton.isVisible()) unpressedButton.draw(canvas);
if (pressedButton.isVisible()) pressedButton.draw(canvas);
if (redButton.isVisible()) redButton.draw(canvas);
}
}
private void readAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.indBar, defStyleAttr, 0);
set_background = a.getInteger(R.styleable.indBar_set_background, def_background);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int chosenWidth = chooseDimension(widthMode, widthSize);
int chosenHeight = chooseDimension(heightMode, heightSize);
setMeasuredDimension(chosenWidth, chosenHeight);
}
private int chooseDimension(int mode, final int size) {
switch (mode) {
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
return size;
case MeasureSpec.UNSPECIFIED:
default:
return SIZE;
}
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
Parcelable superState = bundle.getParcelable("superState");
super.onRestoreInstanceState(superState);
currentLevel = bundle.getInt("barLevel");
indPerc = bundle.getInt("indPerc");
startup = true;
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle state = new Bundle();
state.putParcelable("superState", superState);
state.putInt("barLevel", currentLevel);
state.putInt("indPerc", indPerc);
unpressedButton.getBitmap().recycle();
redButton.getBitmap().recycle();
pressedButton.getBitmap().recycle();
colorBar.getBitmap().recycle();
emptyBar.getBitmap().recycle();
indicator.getBitmap().recycle();
arrow.getBitmap().recycle();
unpressedButton = null;
redButton = null;
pressedButton = null;
colorBar = null;
emptyBar = null;
indicator = null;
arrow = null;
colorbarClip = null;
return state;
}
@Override
public boolean onTouchEvent(MotionEvent e){
int action = e.getActionMasked();
switch (action){
case MotionEvent.ACTION_DOWN: //New Touch
final int ai = MotionEventCompat.getActionIndex(e);
touchY = (int) MotionEventCompat.getY(e, ai);
unpressedButton.setVisible(false, false);
pressedButton.setVisible(true, false);
mActivePointerId = MotionEventCompat.getPointerId(e, 0);
return true;
case MotionEvent.ACTION_MOVE: //Movement
final int ai2 = MotionEventCompat.findPointerIndex(e, mActivePointerId);
int changeY = (int) MotionEventCompat.getY(e, ai2) - touchY;
buttonMove(changeY);
touchY = (int) MotionEventCompat.getY(e, ai2);
invalidate();
return true;
case MotionEvent.ACTION_UP: //Touch ended
unpressedButton.setVisible(true, false);
pressedButton.setVisible(false, false);
return true;
case MotionEvent.ACTION_POINTER_UP:
int pointerIndex = MotionEventCompat.getActionIndex(e);
int pointerId = MotionEventCompat.getPointerId(e, pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
touchY = (int) MotionEventCompat.getY(e, newPointerIndex);
mActivePointerId = MotionEventCompat.getPointerId(e, newPointerIndex);
}
return true;
default:
return super.onTouchEvent(e);
}
}
private void buttonMove(int y){
if(buttonY + y >= backOffset && buttonY2 + y <= view_height - backOffset) {
buttonY += y;
buttonY2 += y;
indY += y;
indY2 += y;
arrowY += y;
arrowY2 += y;
buttonBounds.set(buttonX, buttonY, buttonX2, buttonY2);
indBounds.set(indX, indY, indX2, indY2);
arrowBounds.set(arrowX, arrowY, arrowX2, arrowY2);
indicator.setBounds(indBounds);
arrow.setBounds(arrowBounds);
unpressedButton.setBounds(buttonBounds);
pressedButton.setBounds(buttonBounds);
redButton.setBounds(buttonBounds);
setIndPerc();
postInvalidate();
}
}
private void setIndPerc(){
int part = indY - barY + ((indY2 - indY)/2);
int whole = barY2 - barY;
indPerc = 100 - ((part*100)/whole);
}
private void setLevelPerc(){
if(colorbarClip != null) {
levelPerc = (colorbarClip.getLevel() * 100) / 10000;
}
}
public void setLevel(int level){
if(animateComplete){
oldCurrLevel = level;
oldLevel = currentLevel;
animateComplete = false;
}
currentLevel = level;
}
public int getIndicatorPerc(){ return indPerc; }
public int getLevelPerc(){ return levelPerc; }
private void animateBar(){
if(colorbarClip != null){
int tempLevel;
if(!animateComplete) {
if (oldLevel > oldCurrLevel) {
tempLevel = oldLevel - animateSpeed;
if (tempLevel < oldCurrLevel) {
oldLevel = oldCurrLevel;
animateComplete = true;
} else oldLevel = tempLevel;
} else {
tempLevel = oldLevel + animateSpeed;
if (tempLevel > oldCurrLevel) {
oldLevel = oldCurrLevel;
animateComplete = true;
} else oldLevel = tempLevel;
}
if(oldLevel == oldCurrLevel) animateComplete = true;
if(animateComplete){
if(oldCurrLevel != currentLevel){
animateComplete = false;
oldLevel = oldCurrLevel;
oldCurrLevel = currentLevel;
}
}
}
colorbarClip.setLevel(oldLevel);
setLevelPerc();
postInvalidate();
}
}
}
null错误的logcat:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.drawable.BitmapDrawable.setVisible(boolean, boolean)' on a null object reference
at prospect_industries.viewobject.indBar.onDraw(indBar.java:217)
at android.view.View.draw(View.java:16468)
at android.view.View.updateDisplayListIfDirty(View.java:15398)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.updateDisplayListIfDirty(View.java:15393)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.updateDisplayListIfDirty(View.java:15393)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.updateDisplayListIfDirty(View.java:15393)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.updateDisplayListIfDirty(View.java:15393)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.updateDisplayListIfDirty(View.java:15393)
at android.view.View.getDisplayList(View.java:15420)
at android.view.View.draw(View.java:16190)
at android.view.ViewGroup.drawChild(ViewGroup.java:3713)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3506)
at android.view.View.draw(View.java:16471)
at android.widget.FrameLayout.draw(FrameLayout.java:598)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:3107)
at android.view.View.updateDisplayListIfDirty(View.java:15398)
at android.view.View.getDisplayList(View.java:15420)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:310)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:316)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:355)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2925)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2722)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2309)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1298)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6982)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:821)
at android.view.Choreographer.doCallbacks(Choreographer.java:606)
at android.view.Choreographer.doFrame(Choreographer.java:576)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:807)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6872)
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:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)