I want to save and restore some data for screen orientation changes (portrait/landscape).
For doing it, I implemented onSaveInstanceState and onRestoreInstanceState in my class that holds the list that I want to restore. It seems to be working, and in MyObject class I implemented Parcelable.
The problem is that my object extends GifImageButton and implement Parcelable so I get this error in my object constructors: "There is no default constructor available for pl.droidsonroids.gif.GifImageButton"
public class MyDerivedClass extends MyBaseClass { // extends AppCompatActivity
ArrayList<MyObject> list;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.episode_five);
if(savedInstanceState == null || !savedInstanceState.containsKey("key")) {
String[] colors = {"black", "red"};
String[] numbers = {"one", "two"};
list = new ArrayList<MyObject>();
for(int i = 0; i < numbers.length; i++)
list.add(new MyObject(numbers[i], colors[i]));
}
else {
list = savedInstanceState.getParcelableArrayList("key");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList("key", list);
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle inState) {
list = inState.getParcelableArrayList("key");
super.onSaveInstanceState(inState);
init();
}
public void init() {
list.add(new MyObject("three", "transparent"));
list.add(new MyObject("for", "white"));
}
}
and for the problem, view the code below:
I want to extends GifImageButton but then I get an error "There is no default constructor available for pl.droidsonroids.gif.GifImageButton" at:
public MyObject(String number, String color)
AND public MyObject(Parcel in)
Note: if I remove: "extends GifImageButton" and "public MyObject(Context context, AttributeSet attrs)" then the code is compiled.
class MyObject extends GifImageButton implements Parcelable {
String color;
String number;
public MyObject(Context context, AttributeSet attrs) {
super(context, attrs);
setImageResource(R.drawable.a);
}
public MyObject(String number, String color) {
this.color = color;
this.number = number;
}
private MyObject(Parcel in) {
color = in.readString();
number = in.readString();
}
public int describeContents() {
return 0;
}
#Override
public String toString() {
return number + ": " + color;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(color);
out.writeString(number);
}
public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() {
public MyObject createFromParcel(Parcel in) {
return new MyObject(in);
}
public MyObject[] newArray(int size) {
return new MyObject[size];
}
};
}
Can I extend GifImageButton in my object class that implements Parcelable? if not then how can I fix it?
The error message appears because you need to call a constructor of the superclass in your constructor. If there is no explicit call, the compiler inserts a call to a constructor without arguments. However, all the super class constructors have some arguments, that's why the compilation fails.
In your case I wouldn't implement Parcelable in your class at all. The superclass doesn't implement it, so you'd need to somehow save the state of the superclass also, which will not be possible to do. The superclass is a View, so it retains a reference to the current activity, which can't be put into a Parcel.
What you should do instead is save not the instance itself, but the state you need. Your state is currently expressed by two strings. You can create a separate class State inside MyObject:
static class State implements Parcelable {
private String color;
private String number;
//Parcelable implementation omitted
}
Then you implement Parcelable for it. MyObject will have a field private State state instead of the current two fields, a constructor which takes a State, and a method State getState(), which will return the state. When you need to save the state, you don't save the object, you get its state and save it instead. When you need to restore, you restore the State first, then use it with the constructor to create a new MyObject with the same state as before.
you MyObject class has private constructor MyObject(Parcel in), make it public so compiler can access it
Related
I have the following classes.
public abstract class Thing {
private String appearance;
public void setAppearance(String appearance) {
this.appearance = appearance;
}
}
public abstract class MovableThing extends Thing {
// ASCII representation of MovableThing moving right.
private static String FACE_RIGHT;
// ASCII representation of MovableThing moving left.
private static String FACE_LEFT;
private boolean goingRight;
public MovableThing() {
setAppearance(FACE_RIGHT);
goingRight = true;
// Some other things
public void turnAround() {
goingRight = !goingRight;
if (goingRight) {
setAppearance(FACE_RIGHT);
} else {
setAppearance(FACE_LEFT);
}
}
public class Bird extends MovableThing() {
private static String FACE_RIGHT = "/'/>";
private static String FACE_LEFT = "<\\'\\";
public Bird() {
super();
// Some other things
}
}
I know that this is currently incorrect because in MovableThing, FACE_RIGHT doesn't get assigned anything so when I call super() in Bird, the appearance just gets set to null. How can I work around this? I have multiple animals with different left/right ASCII representations but I'm not sure how to do all of this in an OOP kind of way.
Edit: Meant to say Bird() instead of Chicken().
Here is what I would do with your code to model your scenario:
public abstract class Thing {
private String appearance;
// Require subclasses of Thing to have a defined "going left" and "going right"
// method.
public abstract void setGoingLeft();
public abstract void setGoingRight();
protected final void setAppearance(String appearance) {
this.appearance = appearance;
}
}
public abstract class MovableThing extends Thing {
private boolean goingRight;
public MovableThing() {
setGoingRight();
// Some other things
}
// Require subclasses to define a method that gives me a String showing which
// way they're facing, when I tell them what way they're facing. This allows
// subclasses (like Bird) to each return their own appearances depending on the
// way they are facing.
protected abstract String getAppearance(boolean right);
// Override the "going left" and "going right" methods (and make them final so
// subclasses can't change them). These also modify the "goingRight" field of a
// MovableThing correctly.
#Override
public final void setGoingLeft() {
goingRight = false;
getAppearance(false);
}
#Override
public final void setGoingRight() {
goingRight = true;
getAppearance(true);
}
public void turnAround() {
// If they're going right, turning them around will make them go left and vice
// versa.
if (goingRight)
setGoingLeft();
else
setGoingRight();
}
}
public class Bird extends MovableThing {
private static final String FACE_RIGHT = "/'/>";
private static final String FACE_LEFT = "<\\'\\";
// This method is called by the super class.
#Override
protected String getAppearance(boolean right) {
// If the super class asks for a Bird's appearance when facing right, return
// "FACE_RIGHT". Otherwise, return "FACE_LEFT". (Other animals can return
// different things depending on the way they're facing.)
return right ? FACE_RIGHT : FACE_LEFT;
}
}
The Parcelable docs say that the CREATOR field must be static, but when I try to implement it this way I get the error "Inner classes cannot have static declarations".
I attempted to resolve this by putting my CategoryButton in a separate class (not declaring it as an inner class in MainActivity) but then I couldn't call getApplicationContext() in the constructor to pass to the super.
public class MainActivity extends ActionBarActivity {
private class CategoryButton extends Button implements Parcelable{
private ArrayList<CategoryButton> buttons = null;
private RelativeLayout.LayoutParams params = null;
public CategoryButton(Context context){
super(context);
};
public void setButtons(ArrayList<CategoryButton> buttons){
this.buttons = buttons;
}
public void setParams(RelativeLayout.LayoutParams params){
this.params = params;
}
public ArrayList<CategoryButton> getButtons(){
return this.buttons;
}
public RelativeLayout.LayoutParams getParams(){
return this.params;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeList(buttons);
}
public static final Parcelable.Creator<CategoryButton> CREATOR // *** inner classes cannot have static declarations
= new Parcelable.Creator<CategoryButton>() {
public CategoryButton createFromParcel(Parcel in) {
return new CategoryButton(in); // *** 'package.MainActivity.this' cannot be referenced from a static context
}
public CategoryButton[] newArray(int size) {
return new CategoryButton[size];
}
};
private CategoryButton(Parcel in) {
super(getApplicationContext());
in.readList(buttons, null);
}
}
// ...other activity code
You need to set your CategoryButton as inner static, i.e.
private static class CategoryButton extends Button implements Parcelable {
...
Does Android AIDL support generics?
For example, assume that I have a class Result<T>, where T can be any type including primitives (via autoboxing) or other custom classes such as Car. Any custom classes implement Parcelable as required by Binder.
Then possible AIDL method signatures would be
Result<Car> m1();
Result<Void> m2();
Result<Boolean> m3();
From what I could gather, the AIDL compiler doesn't like things like Result<Animal> getResult();. However, Result getResult(); does work. So this is what I did:
Created a class with the signature public class Result<T extends Parcelable> implements Parcelable.
Created a new class to throw into the first one, which is called Animal. The signature is public class Animal implements Parcelable.
Had to implement methods required by interface Parcelable and a CREATOR in both Result and Animal, and also created one AIDL for each as is required and imported both classes in the main AIDL. This stuff is regular AIDL work and is describe in the AIDL site.
Inside Result, we store not only an object of type T but also a Class object. When writing the parcel we need to write first the class type and only then the generic object. When reading, we do it in the same order. We need to write the class type because when we read we have to do t = (T) in.readValue(classType.getClassLoader()); and without a class type we do not know which class loader to fetch. There are probably other ways to do this but this is how I've done it for this example.
When receiving on the client node, I can successfully do Result<Animal> r = MainActivity.this.service.getResult(); and then call methods on both Result and Animal.
Some code that will hopefully makes things more clearer can be found below.
public class Result<T extends Parcelable> implements Parcelable {
private String msg;
private Class classType;
private T value;
public Result(String msg, T value, Class classType) {
this.msg = msg;
this.value = value;
this.classType = classType;
}
// to reconstruct object
public Result(Parcel in) {
readFromParcel(in);
}
public String getMsg() {
return msg;
}
public T getValue() {
return value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(msg);
dest.writeValue(classType);
dest.writeValue(value);
}
private void readFromParcel(Parcel in) {
this.msg = in.readString();
this.classType = (Class) in.readValue(Class.class.getClassLoader());
this.value = (T) in.readValue(classType.getClassLoader());
}
public static final Creator<Result> CREATOR = new Creator<Result>() {
#Override
public Result createFromParcel(Parcel source) {
return new Result(source);
}
#Override
public Result[] newArray(int size) {
return new Result[size];
}
};
}
public class Animal implements Parcelable {
private int n;
public Animal(int n) {
this.n = n;
}
public Animal(Parcel in) {
readFromParcel(in);
}
public int getN() {
return n;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(n);
}
private void readFromParcel(Parcel in) {
n = in.readInt();
}
public static final Creator<Animal> CREATOR = new Creator<Animal>() {
#Override
public Animal createFromParcel(Parcel source) {
return new Animal(source);
}
#Override
public Animal[] newArray(int size) {
return new Animal[size];
}
};
}
Excerpt from the Service:
#Override
public Result getResult() throws RemoteException {
Result<Animal> r = new Result<Animal>("this is an animal", new Animal(42), Animal.class);
return r;
}
Excerpt from the Client:
Result<Animal> r = MainActivity.this.service.getResult();
Log.d(TAG, "Received the following (Outer): " + r.getMsg());
Log.d(TAG, "Received the following (Inner): " + r.getValue().getN());
Another way to do it is changing the signature of Result into public class Result<T extends Serializable> implements Parcelable, making Animal implement Serializable, and then use dest.writeSerializable(value); and this.value = (T) in.readSerializable(); inside Result.
With this approach there is no need to send the class type to the other side or even use it at all. You will, nonetheless, pay the price.
Daniels solution almost worked for me except the thing with marshalling and unmarshaling classtype.
Instead of "dest.writeValue(classType);" and "this.classType = (Class) in.readValue(Class.class.getClassLoader());" I had to use "dest.writeSerializable(classType);" and "classType = (Class) in.readSerializable();" and it worked like a charm
Thank you Daniel
I'm trying to create public class MyClass<T extends Parcelable> implements Parcelable. I'm having trouble implementing Parcelable. Is it possible to create a generic class that implements Parcelable? (Note that T is bounded so that it also must implement Parcelable).
I am running into trouble with the fact that the Parcelable interface requires a static variable: public static final Parcelable.Creator<MyParcelable> CREATOR. Thus I cannot do public static final Parcelable.Creator<MyClass<T>> CREATOR because MyParcelable<T> is nonstatic.
André
I had similar issues with implementing Parcelable on a class with a generic, the first issue was the same as what you were experiencing:
Thus I cannot do public static final Parcelable.Creator> CREATOR because MyParcelable is nonstatic.
The second was to read in a Parcelable object you need access to the ClassLoader which cannot be gotten from T due to type erasure.
The class below is an adaption of a class I am using in production which overcomes both issues. Note: I have not tested this class specifically, so let me know if you have any issues.
public class TestModel<T extends Parcelable> implements Parcelable {
private List<T> items;
private String someField;
public List<T> items() {
return items;
}
public void setItems(List<T> newValue) {
items = newValue;
}
public String someField() {
return someField;
}
public void setSomeField(String newValue) {
someField = newValue;
}
//region: Parcelable implementation
public TestModel(Parcel in) {
someField = in.readString();
int size = in.readInt();
if (size == 0) {
items = null;
}
else {
Class<?> type = (Class<?>) in.readSerializable();
items = new ArrayList<>(size);
in.readList(items, type.getClassLoader());
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someField);
if (items == null || items.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(items.size());
final Class<?> objectsType = items.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(items);
}
}
public static final Parcelable.Creator<TestModel> CREATOR = new Parcelable.Creator<TestModel>() {
public TestModel createFromParcel(Parcel in) {
return new TestModel(in);
}
public TestModel[] newArray(int size) {
return new TestModel[size];
}
};
//endregion
}
Write the generic data member class name to the parcel and then read it back in order to create its class loader. Example,
public class MyClass<T> implements Parcelable {
T data;
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data.getClass().getName());
dest.writeParcelable((Parcelable) data, 0);
}
private MyClass(Parcel in) {
final String className = in.readString();
try {
data = in.readParcelable(Class.forName(className).getClassLoader());
} catch (ClassNotFoundException e) {
Log.e("readParcelable", className, e);
}
}
Yes you can. You just need to store the class name or class loader during the construction of your subclass object and then you can pass it during the read/write operation of the parcelable.
Step by step instructions:
Step 1. Store the class name that extends from your Generic class like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
Step 2. Any classes that extends from your generic class must specify the class name during its construction like this:
public class MyClass extends GenericClass<MyClass> {
public MyClass () {
super();
setClassName(MyClass.class.getName()); // Generic class setter method
}
Step 3. In your generic class, you can then read/write your class names to getClassLoader() like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
T myGenericObject;
protected MyClass (Parcel in) {
super(in);
this.className = in.readString();
ClassLoader classLoader;
try {
classLoader = Class.forName(this.className).getClassLoader();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
myGenericObject = in.readParcelable(classLoader);
//... Other class members can go here
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(className);
//... Other class members can go here
}
}
Based on answers above, have created extension functions for this.
fun <T : Parcelable> Parcel.writeGenericParcelable(data: T, flags: Int) {
writeString(data::class.java.name)
writeParcelable(data, flags)
}
fun <T : Parcelable> Parcel.readGenericParcelable(): T {
val className = readString()!!
val classNameLoader = Class.forName(className).classLoader
return readParcelable(classNameLoader)!!
}
I have an array named Floors in class A, it contains values something like the following:
List<Point> manualpoint = new ArrayList<Point>();
int[] manualpointx = {135,200,300,155,235,300};
Let's say I wish to pass these values to class B, supposingly class B has already declared a superclass View
public class DrawView extends View implements OnTouchListener {
#Override
public void onDraw(Canvas canvas) {
//pass the values into this class
}
}
How do i pass the values from class A to B?
To pass data between activities
//to pass array use
intent.putExtra("intarray", manualpointx);
// to pass list of point use
intent.putParcelableArrayListExtra("bundle", (ArrayList<? extends Parcelable>) manualpoint);
For classes
create public getter methods to return the values and call those
methods from any class where you want to get the values
You can do something like this.
final class A
{
private static int[] manualpointx = {135,200,300,155,235,300};
private static List<Point> manualpoint = new ArrayList<Point>();
public static int[] returnArray()
{
return(manualpointx);
}
public static List<Point> returnList()
{
return(manualpoint);
}
}
public class DrawView extends View implements OnTouchListener
{
#Override
public void onDraw(Canvas canvas)
{
//pass the values into this class
int arr[]=A.returnArray();
List<Point> list=A.returnList();
}
}
If you need only non-static fields in your class A and if you just want to use non-static methods, you will have to access them via an instance of the class A from your class DrawView.
You need to create an instance of Class A and then access the values from Class B, for example:
public class ClassA {
private int someInt;
public ClassA() {
someInt = 5;
}
public int getSomeInt() {
return someInt;
}
}
public class ClassB {
public void someMethod() {
ClassA instanceOfClassA = new ClassA();
int someIntFromClassA = instanceOfClassA.getSomeInt(); //This is now 5;
// Rest of your code here
}
}
Alternatively you can create it as a static method in ClassA and then call it in ClassB by using ClassA.getSomeInt();
Another one idea is , try having a public class in B and if possible call the B's set Array method with the Array values of A. now in set Array we can have a class variable to save the Array values and then call draw.
public class DrawView extends View implements OnTouchListener {
int[] manualpointx;
setArray(int[] manualpointx1){
this.manualpointx = manualpointx1
}
public void onDraw(Canvas canvas) {
//pass the values into this class
we can use manualpointx
}
}