In my app I am using Rotate3dAnimation to show a Google map. The code is working fine, but the animation is not smooth, some lines are also visible while rotating the view. Please take a look at my code and suggest me how can I make this animation more smoother? Suggestion on achieving this type of animation in any other efficient way is highly appreciated.
public class EventsActivity extends MapActivity implements DialogInterface.OnDismissListener {
private EventsItemModel eventsItemModel;
private Integer eventItemId;
private Integer eventCategoryId;
private static MapOverlay mapOverlay;
Drawable marker;
Context context;
private static String MY_LOCATION = "My Location";
private ViewGroup mContainer;
private ImageView mImageView;
private MapView mMapView;
private static boolean isFlipped = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.event_item_detail);
mContainer = (ViewGroup) findViewById(R.id.event_container);
// Since we are caching large views, we want to keep their cache
// between each animation
mContainer.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);
mMapView = (MapView) findViewById(R.id.mapview);
mImageView = (ImageView) findViewById(R.id.mapPreview);
mImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
isFlipped = true;
applyRotation(1, 0, 90);
}
});
try {
eventCategoryId = getIntent().getIntExtra(AppConstants.EVENT_CATEGORY, 0);
eventItemId = getIntent().getIntExtra(AppConstants.EVENT_ID, 0);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void onResume() {
super.onResume();
WeakReference<EventsActivity> weakContext = new WeakReference<EventsActivity>(this);
EventsAsyncTask task = new EventsAsyncTask(weakContext);
task.execute(eventItemId, eventCategoryId);
}
public void onTaskComplete(EventsItemModel eiModel) {
this.eventsItemModel = eiModel;
TextView calTitle = (TextView) findViewById(R.id.news_title);
TextView eventTitle = (TextView) findViewById(R.id.cal_event_title);
TextView calDate = (TextView) findViewById(R.id.cal_date);
TextView calTime = (TextView) findViewById(R.id.cal_time);
TextView calAddress = (TextView) findViewById(R.id.cal_address);
TextView calDescription = (TextView) findViewById(R.id.cal_description);
try {
calTitle.setText(eventsItemModel.getEventsCategory().getTitle());
calTitle.setVisibility(View.VISIBLE);
eventTitle.setText(eventsItemModel.getEventTitle());
calDate.setText(eventsItemModel.getFormattedDateRange());
// TODO:Format start and end time
calTime.setText("Time: " + eventsItemModel.getFormattedStartTime() + " - " + eventsItemModel.getFormattedEndTime());
calAddress.setText(eventsItemModel.getAddress());
calDescription.setText(eventsItemModel.getDescription());
System.out.println("<<<<<<<<< EventsActivity >>>>>>>>> isRead? " + eventsItemModel.getReadUnread());
eventsItemModel.setReadUnread(true);
System.out.println("<<<<<<<<<< EventsActivity >>>>>>>>>> isRead? " + eventsItemModel.getReadUnread());
}
catch (Exception e) {
e.printStackTrace();
}
mMapView.setBuiltInZoomControls(true);
setMapParameters();
createItemizedOverlay();
setLocationMarker(createMarker(R.drawable.location_marker));
showLocationPointOnMap();
}
@Override
public void onDismiss(DialogInterface dialog) {
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
public void createItemizedOverlay() {
mapOverlay = new MapOverlay(this);
}
public void setLocationMarker(Drawable marker) {
mapOverlay.setLocationMarker(marker);
}
public void showLocationPointOnMap() {
GeoPoint geoPoint = new GeoPoint(0, 0);
if (eventsItemModel != null && eventsItemModel.getLatitude() != null && eventsItemModel.getLatitude().length() > 0 && eventsItemModel.getLongitude() != null
&& eventsItemModel.getLongitude().length() > 0) {
try {
geoPoint = new GeoPoint((int) (Double.parseDouble(eventsItemModel.getLatitude()) * 1E6), (int) (Double.parseDouble(eventsItemModel.getLongitude()) * 1E6));
}
catch (NumberFormatException e) {
e.printStackTrace();
}
OverlayItem item = new OverlayItem(geoPoint, MY_LOCATION, null);
mapOverlay.addItem(item);
mMapView.getOverlays().add(mapOverlay);
// move to location
mMapView.getController().animateTo(geoPoint);
// redraw map
mMapView.postInvalidate();
}
}
public void setStreetView(boolean isStreetView) {
mMapView.setStreetView(isStreetView);
}
public void setSatelliteView(boolean isSatelliteView) {
mMapView.setSatellite(isSatelliteView);
}
public void setZoom(int zoomLevel) {
mMapView.getController().setZoom(zoomLevel);
}
private void setMapParameters() {
// setStreetView(true);
// setSatelliteView(false);
setZoom(17);
}
private Drawable createMarker(int iconID) {
// Initialize icon
Drawable icon = getResources().getDrawable(iconID);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
return icon;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
/**
* Setup a new 3D rotation on the container view.
*
* @param position
* the item that was clicked to show a picture, or -1 to show the list
* @param start
* the start angle at which the rotation must begin
* @param end
* the end angle of the rotation
*/
private void applyRotation(int position, float start, float end) {
// Find the center of the container
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
// Create a new 3D rotation with the supplied parameter
// The animation listener is used to trigger the next animation
final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(new DisplayNextView(position));
mContainer.startAnimation(rotation);
}
/**
* This class listens for the end of the first half of the animation. It then posts a new action that effectively swaps the views when the container is rotated 90 degrees and thus invisible.
*/
private final class DisplayNextView implements Animation.AnimationListener {
private final int mPosition;
private DisplayNextView(int position) {
mPosition = position;
}
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
mContainer.post(new SwapViews(mPosition));
}
public void onAnimationRepeat(Animation animation) {
// Do nothing!!
}
}
/**
* This class is responsible for swapping the views and start the second half of the animation.
*/
private final class SwapViews implements Runnable {
private final int mPosition;
public SwapViews(int position) {
mPosition = position;
}
public void run() {
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
Rotate3dAnimation rotation;
if (mPosition > -1) {
mImageView.setVisibility(View.GONE);
mMapView.setVisibility(View.VISIBLE);
mMapView.requestFocus();
rotation = new Rotate3dAnimation(-90, 180, centerX, centerY, 310.0f, false);
rotation.reset();
}
else {
mMapView.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
mImageView.requestFocus();
rotation = new Rotate3dAnimation(90, 0, centerX, centerY, 310.0f, false);
}
rotation.setDuration(100);
rotation.setFillAfter(true);
rotation.setInterpolator(new DecelerateInterpolator());
mContainer.startAnimation(rotation);
}
}
@Override
public void onBackPressed() {
if (isFlipped) {
applyRotation(-1, 0, -90);
isFlipped = false;
}
else {
super.onBackPressed();
}
}
}
My xml layout is as follows:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/event_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#426773" >
<include
android:id="@+id/news_header"
layout="@layout/news_header" />
<TextView
android:id="@+id/cal_event_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/news_header"
android:padding="5dp"
android:textColor="@android:color/white"
android:textSize="22sp"
android:textStyle="bold"
android:typeface="sans" />
<RelativeLayout
android:id="@+id/date_time_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/cal_event_title">
<TextView
android:id="@+id/cal_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:padding="5dp"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/cal_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/cal_date"
android:padding="5dp"
android:textColor="@android:color/white" />
</RelativeLayout>
<ImageView
android:id="@+id/mapPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/cal_event_title"
android:layout_alignParentRight="true"
android:paddingRight="5dp"
android:clickable="true"
android:src="@drawable/ic_event_map"
android:onClick="showMap"
android:background="@drawable/textview_border"
android:layout_marginRight="5dp"/>
<TextView
android:id="@+id/cal_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/date_time_container"
android:padding="5dp"
android:textColor="@android:color/white"
android:textSize="16sp"
android:textStyle="bold"
android:typeface="sans" />
<ScrollView
android:id="@+id/scroll_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/cal_address"
android:padding="5dp"
android:scrollbars="vertical" >
<RelativeLayout
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/cal_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"/>
</RelativeLayout>
</ScrollView>
<com.google.android.maps.MapView
android:id="@+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:apiKey="your_google_api_key"
android:clickable="true"
android:visibility="gone" />
</RelativeLayout>
To create a rotating animation in CSS, use the animation property and set the value of animations like duration, direction, and speed. Moreover, the rotate() CSS function is being used to rotate an element circularly in the transform property.
To create an endless rotation animation simply define a @keyframe with any name and from and to values. Then you can use this @keyframe in your animation in any CSS class.
Select the 3D layer that you want to turn. Select the Rotation tool , and choose Orientation or Rotation from the Set menu to determine whether the tool affects Orientation or Rotation properties.
I will just give a small hint; however right now I am so busy at work I can not implement this.
The steps are
This I believe will maximize the performance.
I will try to write some code later.
CODE
View longLivingReference; //keep a reference
private void applyRotation(int position, float start, float end) {
longLivingReference = findViewById(R.id.event_container);
longLivingReference .setDrawingCacheEnabled(true);
Bitmap bitmapForAnimation = Bitmap.createBitmap(longLivingReference.getDrawingCache());
ImageView iv = new ImageView(mContext);
iv = new ImageView(mContext);
iv.setImageBitmap(bitmapForAnimation);
setContentView(iv);
final float centerX = mContainer.getWidth() / 2.0f;
final float centerY = mContainer.getHeight() / 2.0f;
final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, centerX, centerY, 310.0f, true);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(yourAnimationListener {
//whatever your AnimationListener is, you can call super.onAnimationEnd if needed
@Override
public void onAnimationEnd(Animation animation) {
setContentView(longLivingReference);
}
});
iv.startAnimation(rotation);
}
I have made the animation like this. I had the same problems. So, suggestions:
make xml layout as simple as possible, you can test it using Hierarchy View tool in android. It tool shows the time of building and drawing the laoyuts;
images on layout should have so low weight as possible;
use hardware acceleration if your device supports it (in manifest):
I have noticed one more interesing behavior. If I call some code in onAnimationEnd(Animation animation) method, the animation freezes for a short time. This problem I was solved using next construction:
private static final int DELAY_AFTER_ANIMATION = 10;
public void onAnimationEnd(Animation animation) { new Handler().postDelayed(new Runnable() { @Override public void run() { setData(); // do the heavy code here } }, DELAY_AFTER_ANIMATION); }
To construct animation I used the same code (Rotate3dAnimation). For calling animation(main difference is using the isReverse parameter):
public void apply3dRotation(float start, float end, AnimationListener listener, boolean isReverse) {
View view = getRotateView();
if(view == null){
return;
}
if (isHardwareAcceleartionNotSupported()){
AndroidHelper.disableHardwareAccelerationOnView(view, this.getClass());
}
final float centerX = view.getWidth() / 2.0f;
final float centerY = view.getHeight() / 2.0f;
Flip3dAnimation rotation;
rotation = new Flip3dAnimation(start, end, centerX, centerY, 310.0f, isReverse);
rotation.setDuration(ANIMATION_DURATION);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
if(listener != null){
rotation.setAnimationListener(listener);
}
view.startAnimation(rotation);
}
isHardwareAcceleartionNotSupported() method checks the OS version. In my project I disabled acceleration for smartphones. In AndroidHelper class:
public static void disableHardwareAccelerationOnView(View view, Class c){
try {
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} catch (Error e) {
Log.i(c.getSimpleName(), e.getMessage());
}
}
And one problem yet. If the animation hides when screen rotates to 90 degrees, it's a problem of a camera. In this case we should to place image farther from the spectator.
And one more trick - sometimes I set the pause before animation start:
animation.setStartOffset(100);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With