I have a layout in which I have dynamically added custom views at a push of a button. These layouts extend LinearLayout and each carry their own unique Action objects.
The views will disappear, however, if onCreate is called again, when the user navigates away or rotates the screen. I want to keep these custom ActionHolder views there. To add to the problem, the ActionHolder objects contain sensitive information. The Action objects themselves store a live timer(that is supposed to keep on ticking even if the app is off), as well as other information.
According to an answer below, I have done the following, but to no avail. Here is what I have so far:
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];
}
};
}
}
Is there SOMETHING I am doing wrong? I've spend almost 4 days already on this.
I have a situation very similar to yours, with custom views being added dynamically to the screen and that need to save state when the activity is killed by the OS and recreated later, for example.
I'm overriding onSaveInstanceState on the custom view. It needs to return a Parcelable object. The key is to create a custom class that extends BaseSavedState and stores your data into that Parcelable. It would look somewhat like this:
#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];
}
};
} }
Also, don't forget to set IDs to your dynamically added views, so they get re-added to the View tree when you come back.
Related
I had a simple spinner which i working perfect. Now I wanted to change it that a user can able to search the items in it. By following the below code I have done changes.
Sample code
//Gradle
compile 'com.toptoche.searchablespinner:searchablespinnerlibrary:1.3.1'
//activity_main.xml
<com.toptoche.searchablespinnerlibrary.SearchableSpinner
android:id="#+id/searchable_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
// In main activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SearchableSpinner searchableSpinner = (SearchableSpinner) findViewById(R.id.searchable_spinner);
String[] names = new String[]{"India","CHINA","UK","US","MALYSIA"};
ArrayAdapter arrayAdapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_spinner_dropdown_item,names);
searchableSpinner.setAdapter(arrayAdapter);
searchableSpinner.setTitle("Select Item");
searchableSpinner.setPositiveButton("OK");
}
Output
On click of the dropdown
What i have done?
//added the library in gradle
compile 'com.toptoche.searchablespinner:searchablespinnerlibrary:1.3.1'
//new_form_layout (i have created this)
<com.toptoche.searchablespinnerlibrary.SearchableSpinner
android:id="#+id/smart_msn_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
**In My Fragment**
#BindView(R.id.smart_msn_spinner)
SearchableSpinner smartMsnSpinner;
Now I have created a bindListners() function in which I am binding all the values and I am calling it in my onCreateView function
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (view == null) {
view = inflater.inflate(R.layout.new_form_layout, container, false);
ButterKnife.bind(this, view);
bindListners(); // here i am calling it
imsiNo.setVisibility(View.GONE);
setupUI(mScrollView);
}
Bundle arguments = getArguments();
if (arguments != null && arguments.containsKey("install_id")) {
isNewInstall = false;
editInstallId = arguments.getString("install_id");
getActivity().setTitle(getString(R.string.title_fragment_edit_form));
setEditData();
imsiNo.setVisibility(View.GONE);
resetFormButton.setVisibility(View.GONE);
} else {
getActivity().setTitle(getString(R.string.title_fragment_new_form));
}
/*mCoordinatesReceiver = new CoordinatesReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Common.GetCoordinatesAction());
getActivity().registerReceiver(mCoordinatesReceiver, intentFilter);*/
return view;
}
bindListeners(){
.......
//Searchable smartMsnSpinner spinner and adapter
meterSrArrayList = new ArrayList<String>();
meterSrNumAdapter = new ArrayAdapter<String>(getActivity(), R.layout.custom_spinner_layout, meterSrArrayList);
smartMsnSpinner.setAdapter(meterSrNumAdapter);
smartMsnSpinner.setTitle("Select Item");
smartMsnSpinner.setPositiveButton("Ok");
smartMsnSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedMeterNo = meterSrArrayList.get(position);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
On running my app i am just getting simple drop down list as before.
I don't know what is the problem and what I am missing as i have done everything that is in the sample.
I have run the sample code in my device and it's working fine. I don't know why it's not working on my app
Update
After watching the logcatthe error i am seeing is
Parcelable encountered IOException writing serializable object (name = com.toptoche.searchablespinnerlibrary.SearchableSpinner)
Any help would be highly appreciated.
I'm using same library but I didn't have this problem.
Anyway I had others problem both on visualization, Parcelization and Re-Initializing the same view (the scroll down view) with this library.
Personally I extended the "SearchableSpinner" and made this changes:
public class BaseSearchableSpinner extends SearchableSpinner
implements SearchView.OnAttachStateChangeListener {
private static final String TAG = BaseSearchableSpinner.class.getSimpleName();
private static final String TAG_DIALOG = TAG.concat(".dialog");
// Dialogs Tags
private static final String TAG_DIALOG_SEARCHABLE_LIST = TAG_DIALOG.concat(".searchableList");
// SearchableSpinner Fields
private static final String FIELD_SEARCHABLE_LIST_DIALOG = "_searchableListDialog";
private static final String FIELD_SEARCH_VIEW = "_searchView";
private static final String FIELD_SEARCHABLE_ITEM = "_searchableItem";
private static final String FIELD_ARRAY_ADAPTER = "_arrayAdapter";
private static final String FIELD_ITEMS = "_items";
private boolean mIsListDialogAdded;
private boolean mIsListenerAdded;
public BaseSearchableSpinner(Context context) {
super(context);
initListDialog();
}
public BaseSearchableSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
initListDialog();
}
public BaseSearchableSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initListDialog();
}
/** Override SearchableSpinner Methods **/
#Override
public boolean onTouch(View v, MotionEvent event) {
try{
SearchableListDialog sld = (SearchableListDialog) FieldUtils.readField(this, FIELD_SEARCHABLE_LIST_DIALOG, true);
ArrayAdapter adapter = (ArrayAdapter) FieldUtils.readField(this, FIELD_ARRAY_ADAPTER, true);
List items = (List) FieldUtils.readField(this, FIELD_ITEMS, true);
if (sld != null && adapter != null && items != null && event.getAction() == MotionEvent.ACTION_UP && checkIfListDialogNotAdded()) {
if(mIsListenerAdded){
mIsListDialogAdded = true;
}
items.clear();
for (int i = 0x0; i < adapter.getCount(); i++) {
items.add(adapter.getItem(i));
}
sld.show(scanForActivity(getContext()).getFragmentManager(), TAG_DIALOG_SEARCHABLE_LIST);
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
return true;
}
/** Override SearchView.OnAttachStateChangeListener Methods **/
#Override
public void onViewAttachedToWindow(View view) {
mIsListDialogAdded = true;
}
#Override
public void onViewDetachedFromWindow(View view) {
mIsListDialogAdded = false;
}
/** Private Methods **/
private void initListDialog(){
try{
SearchableListDialog oldD = (SearchableListDialog) FieldUtils.readField(this, FIELD_SEARCHABLE_LIST_DIALOG, true);
if(oldD != null) {
BaseSearchableListDialog newD = new BaseSearchableListDialog(this);
newD.setArguments(oldD.getArguments());
newD.setOnSearchableItemClickListener(this);
FieldUtils.writeField(this, FIELD_SEARCHABLE_LIST_DIALOG, newD, true);
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
}
private void initListenerOnCloseSearchView(SearchableListDialog instance) {
try{
SearchView sv = (SearchView) FieldUtils.readField(instance, FIELD_SEARCH_VIEW, true);
if(sv != null){
sv.addOnAttachStateChangeListener(this);
mIsListenerAdded = true;
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
}
private boolean checkIfListDialogNotAdded(){
return !mIsListDialogAdded && scanForActivity(getContext()).getFragmentManager().findFragmentByTag(TAG_DIALOG_SEARCHABLE_LIST) == null;
}
private Activity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());
return null;
}
/** Private Classes **/
#SuppressLint("ValidFragment")
public static class BaseSearchableListDialog extends SearchableListDialog {
private BaseSearchableSpinner mOuter;
private BaseSearchableListDialog(BaseSearchableSpinner bss){
super();
mOuter = bss;
}
/** Override SearchableListDialog Methods **/
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog dialog = (AlertDialog) super.onCreateDialog(savedInstanceState);
mOuter.initListenerOnCloseSearchView(this);
return dialog;
}
}
}
Try using this and see if it works.
Also I changed the adapter to have custom texts and don't always display the "toString" of an object :/ I normally use the toString only for debugging purposes, so to show info about an object I make specific methods.
So this is the class for the Adapter:
public abstract class BaseSearchableSpinnerAdapter<T> extends ArrayAdapter<CharSequence> {
// Empty Item Label
protected static final String LABEL_EMPTY_ITEM = " ";
// Label Length
protected static final int LABEL_LENGTH = 50;
// Spinner Adapter Positions
public static final int POS_ITEM_NOT_FOUND = -0x1;
public static final int POS_EMPTY_ITEM = 0x0; // Not always true, depends if implemented
protected List<T> mItems;
private int mResLayout;
public BaseSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource) {
super(context, resource);
mItems = new ArrayList<>();
mResLayout = resource;
}
/** Abstract Methods **/
public abstract <T extends CharSequence> T getLabelView(int pos);
/** Override ArrayAdapter Methods **/
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(mResLayout, parent, false);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.spinner_default, parent, false);
}
}
TextView tv = convertView.findViewById(R.id.text1);
if(tv != null){
tv.setText(getLabelView(position));
}
return convertView;
}
#Override
public void clear(){
mItems.clear();
super.clear();
}
/** Public Methods **/
public void addAll(List<T> objs){
clear();
ArrayList<CharSequence> labels = new ArrayList<>();
if(objs != null && objs.size() > 0x0){
mItems.addAll(objs);
for(int i = 0x0; i < objs.size(); i++){
labels.add(getLabelView(i));
}
}
super.addAll(labels);
}
public T getMyItem(int pos){
if(mItems != null && mItems.size() > pos && pos != -0x1){
return mItems.get(pos);
}
return null;
}
public List<T> getMyItems(){
return mItems;
}
}
Extend this class and use the object you want.
LABEL_EMTPY_ITEM is a long series of spaces because in some app when the text don't take all the line in the list view it will be clickable only on the text and not on all the line.. So when you have no item the clikable part of the line is a little small piece on the left (in my case, I had this problem).
Example to extend this base Adapter class:
public class MyObjectSearchableSpinnerAdapter extends BaseSearchableSpinnerAdapter<MyObject> {
private #StringRes int mIdFstr;
public MyObjectSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource){
this(context, resource, R.string.fstr_two_fields_dash);
}
public MyObjectSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource, int idFstr){
super(context, resource);
mIdFstr = idFstr;
}
/** Override BaseSearchableSpinnerAdapter Methods **/
#Override
public <T extends CharSequence> T getLabelView(int pos) {
MyObject item = mItems.get(pos);
if(item != null){
return (T) (!TextUtils.isEmpty(item.getName2()) ?
getContext().getString(mIdFstr, item.getName1(), item.getName2()) :
item.getName1());
}
return (T) LABEL_EMPTY_ITEM;
}
/** Public Methods **/
public int getItemPosition(int idMyObject){
return getItemPosition(String.valueOf(idMyObject));
}
public int getItemPosition(String idMyObject){
if(mItems != null && mItems.size() > 0x0){
for(int i = 0x0; i < mItems.size(); i++){
MyObject item = mItems.get(i);
if(item != null && idMyObject.equals(item.getId())){
return i;
}
}
}
return POS_ITEM_NOT_FOUND;
}
}
Example Init BaseSearchableSpinner:
private void initBaseSearchableSpinnerMyObjects(){
MyObjectSearchableSpinnerAdapter adapter = new MyObjectSearchableSpinnerAdapter(getContext(), R.layout.spinner_default);
adapter.setDropDownViewResource(R.layout.spinner_default);
mBaseSearchableSpinnerMyObjects.setAdapter(adapter);
}
Example Add your list of MyObject to the adapter:
((MyObjectSearchableSpinnerAdapter)mBaseSearchableSpinnerMyObjects.getAdapter()).addAll(items);
Example Get back an object from a BaseSearchableSpinner with an extensions of BaseSearchableAdapter with a list of MyObject :
MyObject obj = ((MyObjectSearchableSpinnerAdapter) mBaseSearchableSpinnerMyObjects.getAdapter()).getMyItem(mBaseSearchableSpinnerMyObjects.getSelectedItemPosition());
Have a nice coding and day!
bb
I am working on a project where i need to create Image Preview Functionality.For that i have created a recyclerview in which i am passing ArrayList of bitmap and displaying it in recyclerview.Now i am converting that arraylist into base64 string array and want to pass that arraylist into new activity using parcelable.
But i am getting TransactionTooLarge Execption.
Is there another way to pass the array to another activity?
Here is my adapter
public class ImageListAdapter extends RecyclerView.Adapter<ImageListAdapter.ViewHolder> {
private ArrayList<UploadImageModel> mBitmapArray;
private Context context;
private UploadImageModel mUploadImageModel;
private ArrayList<Base64ArrayModel> mBase64ArrayList;
private Base64ArrayModel mBase64ArrayModel;
public ImageListAdapter(ArrayList<UploadImageModel> mBitmapArray, ArrayList<Base64ArrayModel> mBase64ArrayList, Context context) {
this.mBitmapArray = mBitmapArray; //Here i am getting arraylist that contains bitmaps
this.context = context;
}
#Override
public ImageListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(R.layout.image_set, null);
ViewHolder holder = new ViewHolder(itemView);
return holder;
}
#Override
public void onBindViewHolder(ImageListAdapter.ViewHolder holder, int position) {
mUploadImageModel = mBitmapArray.get(position);
holder.UploadImageView.setImageBitmap(mUploadImageModel.getUploadImageBitmap());
holder.UploadImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent openPreviewActivity = new Intent(context, PreviewActivity.class);
openPreviewActivity.putParcelableArrayListExtra("myImageList",encodeList());
context.startActivity(openPreviewActivity);
}
});
}
#Override
public int getItemCount() {
return mBitmapArray.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView UploadImageView;
public ViewHolder(View itemView) {
super(itemView);
UploadImageView = (ImageView) itemView.findViewById(R.id.UploadImageView);
}
}
private ArrayList<Base64ArrayModel> encodeList() {
mBase64ArrayList = new ArrayList<>();
for (int i = 0; i < mBitmapArray.size(); i++) {
mBase64ArrayList.add(new Base64ArrayModel(ConstantFunction.encodeToBase64(mBitmapArray.get(i).getUploadImageBitmap(), Bitmap.CompressFormat.JPEG, 100)));
}
return mBase64ArrayList;
}
}
and the model i am using is as follows
public class Base64ArrayModel implements Parcelable {
public String mBase64BitmapString;
public String getmBase64BitmapString() {
return mBase64BitmapString;
}
public void setmBase64BitmapString(String mBase64BitmapString) {
this.mBase64BitmapString = mBase64BitmapString;
}
public Base64ArrayModel(String mBase64BitmapString)
{
this.mBase64BitmapString=mBase64BitmapString;
}
protected Base64ArrayModel(Parcel in) {
mBase64BitmapString = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mBase64BitmapString);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Base64ArrayModel> CREATOR = new Parcelable.Creator<Base64ArrayModel>() {
#Override
public Base64ArrayModel createFromParcel(Parcel in) {
return new Base64ArrayModel(in);
}
#Override
public Base64ArrayModel[] newArray(int size) {
return new Base64ArrayModel[size];
}
};
}
How can i pass that arrayList to new activity?
From the doc,
During a remote procedure call, the arguments and the return value of
the call are transferred as Parcel objects stored in the Binder
transaction buffer. If the arguments or the return value are too large
to fit in the transaction buffer, then the call will fail and
TransactionTooLargeException will be thrown.
The Binder transaction buffer has a limited fixed size, currently 1Mb,
which is shared by all transactions in progress for the process.
Consequently this exception can be thrown when there are many
transactions in progress even when most of the individual transactions
are of moderate size.
So, this basically means, you're trying to pass data with a size greater than the Binder Transaction Buffer can contain. To overcome this, you've to reduce the size of the data(base64String size, for your case). I can see you've this
ConstantFunction.encodeToBase64(mBitmapArray.get(i).getUploadImageBitmap(), Bitmap.CompressFormat.JPEG, 100) method for encoding a bitmap to base64String where you've passed 100 as compression level. In your implementation, if you use bitmap.compress method to compress the bitmap then try to reduce the number. The less the number the less quality it would get after the compression hence, you'll get small sized base64String in the end.
first, you add this line into your manifest file.
android:largeHeap="true"
Because simultaneously at a time your transaction too large. So make one singleton class like. It is not preferred way I want to suggest use database but if you have not any other choice than this one is better for you.
public class DataTransactionModel {
private static volatile DataTransactionModel instance = null;
private ArrayList<Base64ArrayModel> list = null;
private DataTransactionModel() {
}
public static synchronized DataTransactionModel getInstance() {
if (instance == null) {
synchronized (DataTransactionModel.class) {
if (instance == null) {
instance = new DataTransactionModel();
}
}
}
return instance;
}
public Bitmap getList() {
return list;
}
public void setList(Bitmap bitmap) {
this.bitmap = list;
}
}
Set data into this singleton class and then after get list of images with the help of singleton class methods.
...
#Override
public void onBindViewHolder(ImageListAdapter.ViewHolder holder, int position) {
mUploadImageModel = mBitmapArray.get(position);
holder.UploadImageView.setImageBitmap(mUploadImageModel.getUploadImageBitmap());
holder.UploadImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DataTransactionModel model = DataTransactionModel.getInstance();
model.setList(encodeList());
Intent openPreviewActivity = new Intent(context, PreviewActivity.class);
context.startActivity(openPreviewActivity);
}
});
}
You can use EventBus :
Create event and then post the arraylist and receive wherever you want to..
for E.g
compile 'org.greenrobot:eventbus:3.0.0'
//create class
public class Base64Event {
public final List< Base64ArrayModel > base64Array;
public Base64Event(List< Base64ArrayModel > base64Array){
}
}
//Post
EventBus.getDefault().postSticky(new Base64Event(base64Array));
//Reciever in activity
#Subscribe(threadMode = ThreadMode.MAIN)
public void anyName(Base64Event event) {
event. base64Array //here is the data passed
}
I want to send my data from Activity1 to Activity2 with putExtra.
My Custom object implements Serializable :
public class ARObjectCategory implements Serializable {
private static final long serialVersionUID = 3128594851129501738L;
public int id;
public String name;
public transient ARObjectCategory parent;
public transient SparseArray<ARObjectCategory> children;
public transient Bitmap iconBitmap = null;
public String icon;
private boolean active = false;
public ARObjectCategory(int id, String name, ARObjectCategory parent) {
this.id = id;
this.name = name;
this.parent = parent;
this.children = new SparseArray<>();
}
public void addChild(ARObjectCategory child) {
children.append(child.id, child);
if (getActive())
child.setActive(true);
}
public final ARObjectCategory getChild(int index) {
return children.valueAt(index);
}
public final SparseArray<ARObjectCategory> getChildren() {
return this.children;
}
public final int getParentLast() {
ARObjectCategory parentTemp = this.parent;
while (parentTemp.parent != null) {
parentTemp = parentTemp.parent;
}
return parentTemp.id;
}
public final ARObjectCategory getChildById(int id) {
return children.get(id, null);
}
public final int getChildrenCount() {
return children.size();
}
public Boolean getActive() {
return this.active;
}
public void setActive(Boolean bool) {
this.active = bool;
}
public Bitmap getIconBitmap() {
return iconBitmap;
}
public void setIconBitmap(Bitmap iconBitmap) {
this.iconBitmap = iconBitmap;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
}
Activity1.java (Sender):
ARObjectCategory test1= adapter.getItem(position);
Intent subCat= new Intent(getActivity(), SubCategoriesActivity.class);
subCat.putExtra("test",test1);
subCat.putExtra("selected",position);
startActivity(subCat);
On Debug Mode my object looks ok. SpraseArray got object of AROjcectCategory.
Activity2.java (Reciever):
Bundle extras = getIntent().getExtras();
if (extras != null) {
int selected = extras.getInt("selected", -1);
ARObjectCategory list = (ARObjectCategory) extras.getSerializable("test");
SparseArray<ARObjectCategory> lista = list.getChildren();
}
But when i debug the Activity2. I see that my SpraseArray of children is null
Finally when i remove the transient from my objects.
public transient ARObjectCategory parent;
public transient SparseArray<ARObjectCategory> children;
I am getting the following error on my logcat :
java.lang.RuntimeException: Parcelable encountered IOException
writing serializable object (name =
com.ethos.ar.core.ARObjectCategory) ........
Caused by:
java.io.NotSerializableException: android.util.SparseArray ......
What is the correct way to send Object that has SpraseArray to other Activity.
Tip: Parcelable is not working:
dest.writeParcelable(this.children);
SparseArray<ARObjectCategory> cannot be converted to SparseArray<Object>
Thanks
When you use the transient modifier, that member is excluded from the serialization process. That's the reason you are getting a null object. When you remove the transient modifier then you receive an exception, but why? That's because the "SparseArray" object itself doesn't implement the "Serializable" interface. Remember that in order to an object be serializable then all of its attributes must implement the "Serializable" interface. Now what can you do? You should implement the "Parcelable" interface of Android, but how? If you have problems using the "SparseArray" in the parcelable process then you can parcel it to another object and the recreate the original "SparseArray" object in the creation process.
I need to save the textview attributes such as text,color,background color, padding values in onSaveInstanceState() -
The textview time is in bindView() in Adapter class -
viewHolder.time.setText(strText);
viewHolder.time.setTextColor(0xff000000);
viewHolder.time.setTextSize(17);
viewHolder.time.setVisibility(View.VISIBLE);
viewHolder.time.setBackgroundColor(nColor);
viewHolder.time.setPadding(25,25,25,25);
How do I save them in onSaveInstanceState() and use it in onCreate() when orientation changed to landscape.
I dont want to use android:configChanges since I have different layout for landscape.
EDIT :
In MyAdapter.java,
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
Child child = children.createStopFromCursor(cursor);
MyFragment ndf = new MyFragment();
viewHolder.time.setText(strText);
viewHolder.time.setTextColor(0xff000000);
viewHolder.time.setTextSize(17);
viewHolder.time.setVisibility(View.VISIBLE);
viewHolder.time.setBackgroundColor(nColor);
viewHolder.time.setPadding(25,25,25,25);
ndf.setLandScape(strText,0xff000000,17,nColor);
view.invalidate();
}
}
In MyFragment.java,
public void setLandScape(String time,int time_color,int time_size,int time_Bcolor){
this.delay_time = time;
this.delay_time_color = time_color;
this.delay_time_size = time_size;
this.delay_time_BColor = time_Bcolor;
}
#Override
public void onSaveInstanceState(Bundle outState) {
int[] padding = {25,25,25,25};
TextViewLandscape attributes = new TextViewLandscape(delay_time, delay_time_color, delay_time_size, delay_time_BColor, padding);
ArrayList<TextViewLandscape> list = new ArrayList<TextViewLandscape>();
list.add(attributes);
outState.putParcelableArrayList("keyTextViewAttributes", list);
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mFromSavedInstanceState = true;
TextViewLandscape textViewAttributes = savedInstanceState.getParcelable("keyTextViewAttributes");
}}
In TextViewLandscape.java,
public class TextViewLandscape implements Parcelable {
private String text;
private int textColor;
private int textSize;
private int backgroundColor;
private int[] paddingAttrs = {4};
public TextViewLandscape(String text, int textColor, int textSize, int backgroundColor, int[] paddingAttrs) {
this.text = text;
this.textColor = textColor;
this.textSize = textSize;
this.backgroundColor = backgroundColor;
this.paddingAttrs = paddingAttrs;
}
public TextViewLandscape(Parcel in) {
text = in.readString();
textColor = in.readInt();
textSize = in.readInt();
backgroundColor = in.readInt();
paddingAttrs = in.createIntArray();
}
public static final Creator<TextViewLandscape> CREATOR = new Creator<TextViewLandscape>() {
#Override
public TextViewLandscape createFromParcel(Parcel in) {
return new TextViewLandscape(in);
}
#Override
public TextViewLandscape[] newArray(int size) {
return new TextViewLandscape[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeInt(textColor);
dest.writeInt(textSize);
dest.writeInt(backgroundColor);
dest.writeIntArray(paddingAttrs);
}
}
Still not working in Landscape mode....
In that case, you will have to define some Parcelable class which keeps all your TextView attributes, for instance:
public class TextViewAttributes implements Parcelable {
private String text;
private int textColor;
private int textSize;
private int visibility;
private int backgroundColor;
private int[] paddingAttrs = {4};
public TextViewAttributes(String text, int textColor, int textSize, int visibility, int backgroundColor, int[] paddingAttrs) {
this.text = text;
this.textColor = textColor;
this.textSize = textSize;
this.visibility = visibility;
this.backgroundColor = backgroundColor;
this.paddingAttrs = paddingAttrs;
}
public TextViewAttributes(Parcel in) {
text = in.readString();
textColor = in.readInt();
textSize = in.readInt();
visibility = in.readInt();
backgroundColor = in.readInt();
paddingAttrs = in.createIntArray();
}
public static final Creator<TextViewAttributes> CREATOR = new Creator<TextViewAttributes>() {
#Override
public TextViewAttributes createFromParcel(Parcel in) {
return new TextViewAttributes(in);
}
#Override
public TextViewAttributes[] newArray(int size) {
return new TextViewAttributes[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(text);
dest.writeInt(textColor);
dest.writeInt(textSize);
dest.writeInt(visibility);
dest.writeInt(backgroundColor);
dest.writeIntArray(paddingAttrs);
}
}
This is how you save the values:
#Override
protected void onSaveInstanceState(Bundle outState) {
int[] padding = {0,5,1,3};
TextViewAttributes attributes = new TextViewAttributes("message", 4, 5, 7, 1, padding);
ArrayList<TextViewAttributes> list = new ArrayList<TextViewAttributes>();
list.add(attributes);
outState.putParcelableArrayList("keyTextViewAttributes", list);
super.onSaveInstanceState(outState);
}
And this is how you retrieve them in onCreate method:
if (savedInstanceState != null) {
ArrayList<TextViewAttributes> textViewAttributes = savedInstanceState.getParcelableArrayListExtra("keyTextViewAttributes");
}
Just add configChanges in activity tag in AndroidManifest.xml as gien below:
<activity
android:name=".TextureViewActivity"
android:configChanges="orientation|screenSize"/>
Everything can be saved without a problem in onSaveInstaceState(). The drawback is you will have to use lots of keys for the values and at some point it can become confusing.
Restore them in onCreateView() by checking if the savedInstanceState is not NULL and by using the same keys I had used previously.
EDIT:
In fact you don't really need to save these values. When the screen orientation is changed, the fragment which contains the adapter will be recreated and the adapter as well. But you have your values hard coded in the adapter. Thus, you don't need to save them.
Assume you want to derive your own View class from an existing View implementation, adding a bit of value, hence maintaining a few variables which represent your View's state in a meaningful way.
It would be nice if your View would save its state automatically just like others do (if an ID is assigned) so you would want to override onRestoreInstanceState() and onSaveInstanceState().
Of course, you need to call the respective methods of your base class, and you need to combine your state information with that of your base class.
Obviously, the only safe way to do so is to wrap your super class' Parcelable in an own Parcelable such that the keys won't get mixed up.
Now there's View.BaseSavedState and its interesting getSuperState() method but I somehow fail to understand how this really adds value to just storing the base class' Parcelable in a Bundle along with the derived View's state values and return that. On the other hand, maybe some other system component will expect all InstanceState information to be of type View.AbsSavedState (e.g. such that getSuperState() can be called)?
Any experiences you're willing to share?
To complement James Chen's answer, here is a full example of how to use this method, based on blog article by Charles Harley.
Code from the link:
public class LockCombinationPicker extends LinearLayout {
private NumberPicker numberPicker1;
private NumberPicker numberPicker2;
private NumberPicker numberPicker3;
public LockCombinationPicker(Context context) {
this(context, null);
}
public LockCombinationPicker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LockCombinationPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
loadViews();
}
private void loadViews() {
LayoutInflater.from(getContext()).inflate(R.layout.lock_combination_picker, this, true);
numberPicker1 = (NumberPicker) findViewById(R.id.number1);
numberPicker1.setMinValue(0);
numberPicker1.setMaxValue(10);
numberPicker2 = (NumberPicker) findViewById(R.id.number2);
numberPicker2.setMinValue(0);
numberPicker2.setMaxValue(10);
numberPicker3 = (NumberPicker) findViewById(R.id.number3);
numberPicker3.setMinValue(0);
numberPicker3.setMaxValue(10);
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return new SavedState(superState, numberPicker1.getValue(), numberPicker2.getValue(), numberPicker3.getValue());
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
numberPicker1.setValue(savedState.getNumber1());
numberPicker2.setValue(savedState.getNumber2());
numberPicker3.setValue(savedState.getNumber3());
}
#Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// As we save our own instance state, ensure our children don't save and restore their state as well.
super.dispatchFreezeSelfOnly(container);
}
#Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
/** See comment in {#link #dispatchSaveInstanceState(android.util.SparseArray)} */
super.dispatchThawSelfOnly(container);
}
/**
* Convenience class to save / restore the lock combination picker state. Looks clumsy but once created is easy to maintain and use.
*/
protected static class SavedState extends BaseSavedState {
private final int number1;
private final int number2;
private final int number3;
private SavedState(Parcelable superState, int number1, int number2, int number3) {
super(superState);
this.number1 = number1;
this.number2 = number2;
this.number3 = number3;
}
private SavedState(Parcel in) {
super(in);
number1 = in.readInt();
number2 = in.readInt();
number3 = in.readInt();
}
public int getNumber1() {
return number1;
}
public int getNumber2() {
return number2;
}
public int getNumber3() {
return number3;
}
#Override
public void writeToParcel(Parcel destination, int flags) {
super.writeToParcel(destination, flags);
destination.writeInt(number1);
destination.writeInt(number2);
destination.writeInt(number3);
}
public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
I think the design needs us, and as the name implies, to implement a subclass of View.BaseSavedState to store values by overriding Parcelable's interface.
TextView.SavedState is a good example
public static class SavedState extends BaseSavedState {
int selStart;
int selEnd;
CharSequence text;
boolean frozenWithFocus;
CharSequence error;
SavedState(Parcelable superState) {
super(superState);
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(selStart);
out.writeInt(selEnd);
out.writeInt(frozenWithFocus ? 1 : 0);
TextUtils.writeToParcel(text, out, flags);
if (error == null) {
out.writeInt(0);
} else {
out.writeInt(1);
TextUtils.writeToParcel(error, out, flags);
}
}
#Override
public String toString() {
String str = "TextView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " start=" + selStart + " end=" + selEnd;
if (text != null) {
str += " text=" + text;
}
return str + "}";
}
#SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private SavedState(Parcel in) {
super(in);
selStart = in.readInt();
selEnd = in.readInt();
frozenWithFocus = (in.readInt() != 0);
text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
if (in.readInt() != 0) {
error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
}
}