【问题标题】:Saving dynamically added LinearLayouts without using savedInstanceState?在不使用 savedInstanceState 的情况下保存动态添加的 LinearLayouts?
【发布时间】:2013-09-12 06:54:50
【问题描述】:

我有一个布局,只需按一下按钮,我就可以在其中动态添加自定义视图。这些布局扩展了 LinearLayout,每个布局都有自己独特的 Action 对象。

但是,如果再次调用 onCreate,当用户离开或旋转屏幕时,视图将会消失。我想将这些自定义 ActionHolder 视图保留在那里。更糟糕的是,ActionHolder 对象包含敏感信息。 Action 对象本身存储一个实时计时器(即使应用程序关闭,它也应该继续计时),以及其他信息。

根据下面的答案,我做了以下操作,但无济于事。这是我目前所拥有的:

public class ActionHolder extends LinearLayout implements Serializable {

   /**
    * 
    */
   private static final long serialVersionUID = 2271402255369440088L;
   private Action action;
   private String timer;
   public static final int ACTION_TITLE = 0, ACTION_TIMER = 1,
         PAUSEANDPLAY_BTN = 2, FINISH_BTN = 3;



   public ActionHolder(Context context) {
      super(context);
   }



   public ActionHolder(Context context, AttributeSet attr) {
      super(context, attr);
   }



   public ActionHolder(Context context, AttributeSet attr, int defStyle) {
      super(context, attr, defStyle);
   }



   public void initiate(Action input) {
      // int hashedID = input.getActionName().hashCode();
      // if (hashedID < 0)
      // hashedID *= -1;
      // this.setId(hashedID);
      this.setOrientation(LinearLayout.VERTICAL);
      this.setLayoutParams(new LinearLayout.LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
      action = input;
      LayoutInflater inflater = LayoutInflater.from(getContext());
      View view = inflater.inflate(R.layout.action_holder_layout, this, true);

      TextView actionTitle = (TextView) view
            .findViewById(com.tonimiko.mochi_bean.R.id.action_holder_title);
      actionTitle.setText(action.getActionName());
      actionTitle.setId(ActionHolder.ACTION_TITLE);

      TextView actionTimer = (TextView) view
            .findViewById(R.id.action_holder_timer);
      actionTimer.setId(ActionHolder.ACTION_TIMER);

      Button pauseBtn = (Button) view
            .findViewById(com.tonimiko.mochi_bean.R.id.pause_and_play_timer_btn);
      pauseBtn.setId(ActionHolder.PAUSEANDPLAY_BTN);

      Button finishBtn = (Button) view
            .findViewById(com.tonimiko.mochi_bean.R.id.finish_activity_button);
      finishBtn.setId(ActionHolder.FINISH_BTN);

      action.setActivityStartTime();
   }



   public Action finishAction() {
      action.setActivityStopTime();
      return action;
   }



   @Override
   protected void onLayout(boolean changed, int l, int t, int r, int b) {
      super.onLayout(changed, l, t, r, b);
   }



   public String toString() {
      return "Action stored: " + action.getActionName();
   }



   @Override
   public boolean equals(Object other) {
      ActionHolder otherObj = (ActionHolder) other;
      if (this.action.getActionName().toUpperCase()
            .equals(otherObj.action.getActionName().toUpperCase()))
         return true;
      return false;
   }



   @Override
   public int hashCode() {
      return action.getActionName().hashCode();
   }



   @Override
   protected Parcelable onSaveInstanceState() {
      Parcelable superState = super.onSaveInstanceState();
      Bundle data = new Bundle();
      data.putString("Timer", timer);
      data.putSerializable("Action", action);
      Log.e("debug", "View onSaveInstanceState called!"); // TODO
      Parcelable test = new ActionHolderSavedState(superState, data);
      if(test==null)
         Log.e("debug", "NULL PARCELABLE"); // TODO
      return new ActionHolderSavedState(superState, data);
   }



   @Override
   protected void onRestoreInstanceState(Parcelable state) {
      Log.e("debug", "View onRestore called!");
      if (state instanceof ActionHolderSavedState) {
         final ActionHolderSavedState savedState = (ActionHolderSavedState) state;
         this.action = savedState.getAction();
         this.timer = savedState.getTimer();
         // this.initiate(action);
         super.onRestoreInstanceState(savedState.getSuperState());
         Log.e("debug", "View onRestoreInstanceState finished"); // TODO
      }
   }

   static class ActionHolderSavedState extends BaseSavedState {

      private Action storedAction;
      private String storedTimer;



      public ActionHolderSavedState(Parcelable superState, Bundle data) {
         super(superState);
         storedTimer = data.getString("Timer");
         storedAction = (Action) data.getSerializable("Action");
      }



      private ActionHolderSavedState(Parcel in) {
         super(in);
         storedTimer = in.readString();
         storedAction = in.readParcelable(ActionHolder.class.getClassLoader());
      }



      public Action getAction() {
         return storedAction;
      }



      public String getTimer() {
         return storedTimer;
      }



      @Override
      public void writeToParcel(final Parcel out, final int flags) {
         super.writeToParcel(out, flags);
         out.writeString(storedTimer);
         out.writeSerializable(storedAction);
      }

      // required field that makes Parcelables from a Parcel
      public static final Parcelable.Creator<ActionHolderSavedState> CREATOR = new Parcelable.Creator<ActionHolderSavedState>() {

         public ActionHolderSavedState createFromParcel(final Parcel in) {
            return new ActionHolderSavedState(in);
         }



         public ActionHolderSavedState[] newArray(int size) {
            return new ActionHolderSavedState[size];
         }
      };
   }
}

我做错了什么吗?我已经花了将近 4 天的时间。

【问题讨论】:

    标签: android android-layout onresume onpause persistent-storage


    【解决方案1】:

    我的情况与您的情况非常相似,例如,自定义视图被动态添加到屏幕上,并且需要在 Activity 被操作系统终止并稍后重新创建时保存状态。

    我在自定义视图上覆盖 onSaveInstanceState。它需要返回一个Parcelable 对象。关键是创建一个自定义类来扩展BaseSavedState 并将您的数据存储到Parcelable 中。它看起来有点像这样:

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable state = super.onSaveInstanceState();
        return new ContainerLayoutSavedState(state, data);
    }
    
    @Override
    protected void onRestoreInstanceState(final Parcelable state) {
        if (state instanceof ContainerLayoutSavedState) {
            final ContainerLayoutSavedState savedState = (ContainerLayoutSavedState)state;
            this.data = savedState.getData();
            super.onRestoreInstanceState(savedState.getSuperState());
        }
    }
    
    public static class ContainerLayoutSavedState extends BaseSavedState {
        private String data;
    
        ContainerLayoutSavedState(final Parcelable superState, final String data) {
            super(superState);
            // Here in this constructor you inject whatever you want to get saved into the Parcelable object. In this contrived example, we're just saving a string called data.
            this.data = data;
        }
    
    
        private ContainerLayoutSavedState(final Parcel in) {
            super(in);
    
            data = in.readString();
        }
    
        public String getData()
            return data;
        }
    
        @Override
        public void writeToParcel(final Parcel out, final int flags) {
            super.writeToParcel(out, flags);
    
            out.writeString(data);
        }
    
        // required field that makes Parcelables from a Parcel
        public static final Parcelable.Creator<ContainerLayoutSavedState> CREATOR = new Parcelable.Creator<ContainerLayoutSavedState>() {
            @Override
            public ContainerLayoutSavedState createFromParcel(final Parcel in) {
                return new ContainerLayoutSavedState(in);
            }
    
            @Override
            public ContainerLayoutSavedState[] newArray(final int size) {
                return new ContainerLayoutSavedState[size];
            }
        };
    } }
    

    此外,不要忘记为您动态添加的视图设置 ID,以便在您返回时将它们重新添加到视图树中。

    【讨论】:

    • 我无法理解这里发生了什么。 1. onSaveInstanceState 返回一个新初始化的包含ContainerLayoutSavedState 的Parcelable。 2. ContainerLayoutSavedState 包含一个数据字段……这个数据是什么?我要保存的 LinearLayout? 3. onRestoreInstanceState 获取接收到的包并重新种植数据。但是等等,如果存储的数据是一个 ActionHolder 对象(它扩展了 LinearLayout),那么我如何通过 this.data = savedState.getData(); 恢复它?我必须自己定义 getData() 方法,不是吗?
    • 我使用的data 属性只是为了说明您将使用此代码执行的操作。但是,是的,基本上你ContainerLayoutSavedState 将是一个将你的LinearLayout 从/恢复到Parcelable 对象的人。为了完整起见,我将编辑上面的代码。这个概念是 ContainerLayoutSavedState 对象负责保存您想要从 LinearLayout 对象中持久保存的任何实例数据
    • 现在我得到了一个 LinearLayout.IllegalAccessException。顺便说一句,我也刚刚更新了我的代码。你能看看我有什么问题吗?
    • 这看起来是正确的,只是您不需要创建Bundle 来传递给ContainerLayoutSavedState,但这看起来不是问题。你能粘贴整个堆栈跟踪吗?
    • 呃...我究竟如何从 LogCat 复制?无论如何只有两行重复: android.widget.LinearLayout; IllegalAccessException android.widget;非法访问异常
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-11
    • 2022-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-20
    相关资源
    最近更新 更多