I am writing a small animation project. I was having the issue that on screen orientation change, it was crashing. But I figured it out, so posting whole code here in the case someone needs something similar. Last sentence at the bottom explains a small issue I observed.
In this project, I have main activity hosting a fragment. And the fragment is hosing a custom Bubble view (Bubble extends View).
Main activity layout activity_main.xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/bubble_fragment">
</LinearLayout>
, and main activity MainActivity.java is:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String tag = "bubble_fragment_tag";
FragmentManager fm = getFragmentManager();
if (fm.findFragmentByTag(tag) == null){
FragmentTransaction ft = getFragmentManager().beginTransaction();
BubbleFragment bubbleFragment = new BubbleFragment();
ft.add(R.id.bubble_fragment, bubbleFragment, tag);
ft.commit();
}
}
}
So, my main activity host a fragment with custom view in it. The fragment layout bubble_fragment.xml is:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="#ffcc33">
<study.android.dino.testsolar.Bubble
android:id="@+id/bubbleAnimationView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff1122"
android:padding="20dp"/>
<!-- since parent is FrameLayout, this view will be stacked up in z-order -->
<LinearLayout
android:id="@+id/buttonsView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="right|bottom"
android:layout_margin="10dp">
<ImageButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:background="@null"/>
<ImageButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:background="@null"/>
<ImageButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
android:background="@null"/>
</LinearLayout>
</FrameLayout>
, and my BubbleFragment.java file is: UPDATE: This is updated BubbleFragment that resolves the problem
public class BubbleFragment extends Fragment {
View view;
Bubble bubble;
FrameLayout parent;
LinearLayout floater;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
if (bubble == null){
view = inflater.inflate(R.layout.bubble_fragment, container, false);
parent = (FrameLayout) view;
}
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
if (parent != null){
parent.addView(bubble);
parent.addView(floater);
}
}
@Override
public void onDetach(){
super.onDetach();
bubble = (Bubble) view.findViewById(R.id.bubbleAnimationView);
floater = (LinearLayout) parent.findViewById(R.id.buttonsView);
parent.removeView(bubble);
parent.removeView(floater);
}
}
This fragment holds custom view Bubble which is just a bubble drawing on Canvas and is not important here; hence not full code for it provided:
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;
private int speed = 200;
//handler to invalidate (force redraw on main GUI thread from this thread)
private Handler handler = new Handler();
public Bubble(Context context, AttributeSet attributesSet) {
super(context, attributesSet);
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(speed);
} 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);
}
}
}
This all works and my bubble draws and moves on the screen and orientation works as well. The only 'small' issue I observed is that speed at which bubble is drawn is set to 200ms as per code above. However, when I change orientation, the bubble is blinking faster although tracing code reviled that speed is still 200. Not sure why that is happening.
Thanks
I updated my original post to show solution.