Android编程——自定义View类onSaveInstanceState与onRestoreInstanceState使用方法

      在很多时候我们为获得在视图中自由绘制的能力, 需要创建一个继承于View类的定制类,然后重写onTouchEvent方法处理触摸时间,重写onDraw绘制自定义视觉效果。但这里可能会被一个问题困扰,那就是设备旋转导致数据丢失的问题,好在View类为我们提供了onSaveInstanceState和onRestoreInstanceState两个方法,虽然这两个方法和Activity两个方法很相似,但是千万别认为是一样的,因为他们的使用方法完全不同。

为了侧重数据保存的重点,我们这里简化了绘制的内容。下面定制的View子类功能是根据触摸事件发生的始末位置,绘制一组矩形。详细观察以下rectDrawingView定制类源码:

package com.art.zok.rectdrawingview;

import java.util.ArrayList;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class RectDrawingView extends View {
	private static final String TAG = "RectDrawingView";
	
	private RectF mCurRect;
	private ArrayList mRects = new ArrayList();
	private Paint mRectPaint;

	/**
	 * 从代码中创建视图.
	 * @author artzok
	 * @param context 设备上下文
	 **/
	public RectDrawingView(Context context) {
		super(context);
 	}
	
	/**
	 * 从xml布局文件中创建视图.
	 * @author artzok
	 * @param context 设备上下文
	 * @param attrs xml属性集
	 **/
	public RectDrawingView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mRectPaint = new Paint();
		mRectPaint.setColor(0x22ff0000);
	}
	
	
	@SuppressLint("ClickableViewAccessibility") @Override
	public boolean onTouchEvent(MotionEvent event) {
		PointF curr = new PointF(event.getX(), event.getY());
		
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mCurRect = new RectF();
			mCurRect.left = curr.x;
			mCurRect.top  = curr.y;
			mRects.add(mCurRect);
			break;
		case MotionEvent.ACTION_MOVE:
			if(mCurRect != null) {
				mCurRect.right = curr.x;
				mCurRect.bottom = curr.y;
				invalidate();
			}
			break;
		case MotionEvent.ACTION_UP:
			mCurRect = null;
			break;
		case MotionEvent.ACTION_CANCEL:
			mCurRect = null;
			break;
		default:
			break;
		}
		return true;	
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		for(RectF rect : mRects) {
			float left = Math.min(rect.left, rect.right);
			float right = Math.max(rect.left, rect.right);
			float top = Math.min(rect.top, rect.bottom);
			float bottom = Math.max(rect.top, rect.bottom);
			canvas.drawRect(left, top, right, bottom, mRectPaint);
		}
		
		super.onDraw(canvas);
	}
	
	@Override
	protected Parcelable onSaveInstanceState() {
		Bundle bundle = new Bundle();
		Parcelable superData = super.onSaveInstanceState();
		bundle.putParcelable("super_data", superData);
		bundle.putParcelableArrayList("save_data", mRects);
		return bundle;
	}
	
	@Override
	protected void onRestoreInstanceState(Parcelable state) {
		Bundle bundle = (Bundle) state;
		Parcelable superData = bundle.getParcelable("super_data");
		mRects = bundle.getParcelableArrayList("save_data");
		super.onRestoreInstanceState(superData);
	}
}

目前不需要研究上述代码,只要获得RectDrawingView即可。然后再布局文件中静态创建该组件。


现在我们来解释上述代码,首先如果我们要从XML布局文件创建RectDrawingView类那么就必须必须创建一个RectDrawingView(Context context, AttributeSet attrs)类,因为Android在实例XML文件中的组件时是通过调用上述构造方法实现的。然而,RectDrawingView(Context context)构造方法一般也需要添加,因为说不定某些时候我们就需要从代码中动态创建组件,所以为了统一情况,我们在自定义View子类时都会创建以上两个构造方法。

接下来在XML布局文件中声明RectDrawingView组件,需特别注意的是,android:id属性必须声明,如果没有为组件添加id,那么重写的onSaveInstanceState和onRestoreInstanceState方法是不会调用的。

关于onTouchEvent和onDraw方法这里不详细说了,很简单,如果需要请自行百度。现在主要集中在onSaveInstanceState和onRestoreInstanceState方法上,首先这两个方法与Activiy像似的两个方法最大的区别在于参数与返回值,View使用Parcelable传递数据,而Activity使用Bundle传递数据,很明显Bundle传递数据更为简单和强大,如果使用Parcelable对象传递自定义数据就需要实现Parcelable接口和CREATE静态常量,不但复杂而且代码量也很大。然而我们这里使用的RectF类作为基础数据,RectF是Android提供的并且已经实现了Parcelable接口和CREATE常量,这方便了许多。但是在onSaveInstanceState方法中,我们必须将父类保存的数据也保存在parcelable对象中,否则应用将发生崩溃。我了避免再次自定义Parcelable子类,因此我们这里使用Bundle将superData = super.onSaveInstanceState();父类保存数据和mRects自定义数据保存在其中,由于Bundle也是实现了Parcelable接口和CARETE常量的,因此在View的两个方法中传递是没有问题的。在onRestoreInstanceState方法中,我们需要将state强制转化成Bundle对象,如果为了安全一点,这里可以使用类型检查,然后将superData从Bundle对象中取出,再使用superData作为参数调用super.onRestoreInstanceState(superData);,相同的mRects也从Bundle中取出进行初始化,需要注意一点,这里千万不能将参数state传递给super.onRestoreInstanceState(state);方法,否则应用同样会崩溃。

Android编程——自定义View类onSaveInstanceState与onRestoreInstanceState使用方法_第1张图片    Android编程——自定义View类onSaveInstanceState与onRestoreInstanceState使用方法_第2张图片














你可能感兴趣的:(Android编程——自定义View类onSaveInstanceState与onRestoreInstanceState使用方法)