I am writing an app that will draw a bubble on Canvas. I have MainActivity with its layout as a simple LinearLayout which I use as holder for fragment. My fragment has no xml layout as I am drawing on Canvas, so I set its layout programmatically like this:
public class BubbleFragment extends Fragment {
Bubble bubble;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//retain fragment
setRetainInstance(true);
//bubble = new Bubble(getActivity()); //THIS WILL CRASH APP, MOVE TO onCreateView instetad
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
100);
LinearLayout ll = new LinearLayout(getActivity());
ll.setOrientation(LinearLayout.VERTICAL);
// instantiate my class that does drawing on Canvas
bubble = new Bubble(getActivity());
bubble.setLayoutParams(lp);
bubble.setBackgroundColor(Color.BLUE);
ll.addView(bubble); //if you create bubble in onCreate() this will crash. Create bubble in onCreateView
return ll;
}
}
So, when I start my app, I am expecting bubble to show in the middle of the screen and move slowly towards bottom. Since I use setRetainInstance(true) above, I am expecting that when I rotate my screen, the bubble will continue where it left off before rotation. However, it is redraws at its initial location (middle of the screen).
I would like to to continue drawing itself from the position where it was before screen orientation changed, not from the beginning.
Here is my bubble code:
public class Bubble extends View {
private static final boolean BUBBLING = true; //thread is running to draw
private Paint paint;
private ShapeDrawable bubble;
// coordiantes, radius etc
private int x;
private int y;
private int dx;
private int dy;
private int r;
private int w = 400;
private int h = 400;
//handler to invalidate (force redraw on main GUI thread from this thread)
private Handler handler = new Handler();
public Bubble(Context context) {
super(context);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
w = size.x;
h = size.y;
x = w/2;
y = h/2;
r = 60;
dx = 1;
dy = 1;
bubble = new ShapeDrawable(new OvalShape());
bubble.getPaint().setColor(Color.RED);
bubble.setBounds(0, 0, r, r);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(10);
}
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh){
//set bubble parameters (center, size, etc)
startAnimation();
}
public void startAnimation(){
new Thread(new Runnable() {
public void run() {
while (BUBBLING) {
moveBubble();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
//update by invalidating on main UI thread
handler.post(new Runnable() {
public void run() {
invalidate();
}
});
}
}
}).start();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// draws bubble on canvas
canvas.translate(dx, dy);
bubble.draw(canvas);
canvas.restore();
}
private void moveBubble(){
dx += 1;
dy += 1;
x = x + dx;
y = y + dy;
if (bubble.getPaint().getColor() == Color.YELLOW){
bubble.getPaint().setColor(Color.RED);
} else {
bubble.getPaint().setColor(Color.YELLOW);
}
}
}
Much appreciated,
If you really want to retain an entire view, you could do something like a lazy load:
In your case you don't need setRetainInstance(true), you just need to save instance variables in onSaveInstanceState() and load them in onCreate(). For your example, something like:
By the way, the reason you can't create Bubble in onCreate() is because the Fragment is not fully associated with its Activity until onActivityCreated() is called, which comes after onCreate() is called.