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

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

 

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

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

[java]  view plain  copy
  1. package com.art.zok.rectdrawingview;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import android.annotation.SuppressLint;  
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Paint;  
  9. import android.graphics.PointF;  
  10. import android.graphics.RectF;  
  11. import android.os.Bundle;  
  12. import android.os.Parcelable;  
  13. import android.util.AttributeSet;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16.   
  17. public class RectDrawingView extends View {  
  18.     private static final String TAG = "RectDrawingView";  
  19.       
  20.     private RectF mCurRect;  
  21.     private ArrayList<RectF> mRects = new ArrayList<RectF>();  
  22.     private Paint mRectPaint;  
  23.   
  24.     /** 
  25.      * 从代码中创建视图. 
  26.      * @author artzok 
  27.      * @param context 设备上下文 
  28.      **/  
  29.     public RectDrawingView(Context context) {  
  30.         super(context);  
  31.     }  
  32.       
  33.     /** 
  34.      * 从xml布局文件中创建视图. 
  35.      * @author artzok 
  36.      * @param context 设备上下文 
  37.      * @param attrs xml属性集 
  38.      **/  
  39.     public RectDrawingView(Context context, AttributeSet attrs) {  
  40.         super(context, attrs);  
  41.         mRectPaint = new Paint();  
  42.         mRectPaint.setColor(0x22ff0000);  
  43.     }  
  44.       
  45.       
  46.     @SuppressLint("ClickableViewAccessibility"@Override  
  47.     public boolean onTouchEvent(MotionEvent event) {  
  48.         PointF curr = new PointF(event.getX(), event.getY());  
  49.           
  50.         switch (event.getAction()) {  
  51.         case MotionEvent.ACTION_DOWN:  
  52.             mCurRect = new RectF();  
  53.             mCurRect.left = curr.x;  
  54.             mCurRect.top  = curr.y;  
  55.             mRects.add(mCurRect);  
  56.             break;  
  57.         case MotionEvent.ACTION_MOVE:  
  58.             if(mCurRect != null) {  
  59.                 mCurRect.right = curr.x;  
  60.                 mCurRect.bottom = curr.y;  
  61.                 invalidate();  
  62.             }  
  63.             break;  
  64.         case MotionEvent.ACTION_UP:  
  65.             mCurRect = null;  
  66.             break;  
  67.         case MotionEvent.ACTION_CANCEL:  
  68.             mCurRect = null;  
  69.             break;  
  70.         default:  
  71.             break;  
  72.         }  
  73.         return true;      
  74.     }  
  75.       
  76.     @Override  
  77.     protected void onDraw(Canvas canvas) {  
  78.         for(RectF rect : mRects) {  
  79.             float left = Math.min(rect.left, rect.right);  
  80.             float right = Math.max(rect.left, rect.right);  
  81.             float top = Math.min(rect.top, rect.bottom);  
  82.             float bottom = Math.max(rect.top, rect.bottom);  
  83.             canvas.drawRect(left, top, right, bottom, mRectPaint);  
  84.         }  
  85.           
  86.         super.onDraw(canvas);  
  87.     }  
  88.       
  89.     @Override  
  90.     protected Parcelable onSaveInstanceState() {  
  91.         Bundle bundle = new Bundle();  
  92.         Parcelable superData = super.onSaveInstanceState();  
  93.         bundle.putParcelable("super_data", superData);  
  94.         bundle.putParcelableArrayList("save_data", mRects);  
  95.         return bundle;  
  96.     }  
  97.       
  98.     @Override  
  99.     protected void onRestoreInstanceState(Parcelable state) {  
  100.         Bundle bundle = (Bundle) state;  
  101.         Parcelable superData = bundle.getParcelable("super_data");  
  102.         mRects = bundle.getParcelableArrayList("save_data");  
  103.         super.onRestoreInstanceState(superData);  
  104.     }  
  105. }  

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

[html]  view plain  copy
  1. <com.art.zok.rectdrawingview.RectDrawingView  
  2.         xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/RectDrawingView"  
  4.         android:layout_width="match_parent"  
  5.         android:layout_height="match_parent" />  

现在我们来解释上述代码,首先如果我们要从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);方法,否则应用同样会崩溃。

    

[java]  view plain  copy
  1.   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值