Как сделать Rotate3dAnimation более плавным?
В моем приложении я использую Rotate3dAnimation, чтобы показать карту Google. Код работает нормально, но анимация негладкая, некоторые строки также видны при повороте представления. Пожалуйста, взгляните на мой код и предложите мне, как сделать эту анимацию более гладкой? Предложение по достижению этого типа анимации любым другим эффективным способом высоко ценится. ![enter image description here]()
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();
}
}
}
Мой xml-макет выглядит следующим образом:
<?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>
Ответы
Ответ 1
Я просто дам небольшой намек; однако сейчас я настолько занят работой, что не могу это реализовать.
Этапы
- получить растровый кэш чертежа
- установите контент только в изображение с помощью этого растрового изображения
- применить анимацию к этому представлению изображений
- в конце анимации установите свой контент
Это, я считаю, максимизирует производительность.
Я попытаюсь позже написать код.
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);
}
Ответ 2
Я сделал анимацию следующим образом. У меня были те же проблемы. Итак, предложения:
- сделайте xml-макет максимально простым, вы можете протестировать его с помощью инструмента Hierarchy View в android. Этот инструмент показывает время построения и рисования лаутов;
- изображения на макете должны иметь такой низкий вес, насколько это возможно;
- используйте аппаратное ускорение, если ваше устройство поддерживает его (в манифесте):
<activity android:name=".ActivityName" android:hardwareAccelerated="true"/>
- Я заметил еще одно интересное поведение. Если я вызову некоторый код в методе onAnimationEnd (анимационная анимация), анимация замерзает на короткое время. Эта проблема была решена с использованием следующей конструкции:
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);
}
Для создания анимации я использовал тот же код (Rotate3dAnimation).
Для вызова анимации (основное отличие заключается в использовании параметра isReverse):
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() проверяет версию ОС. В моем проекте я отключил ускорение для смартфонов.
В классе AndroidHelper:
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());
}
}
И еще одна проблема. Если анимация скрывается, когда экран поворачивается на 90 градусов, это проблема камеры. В этом случае мы должны поместить изображение дальше от зрителя.
Ответ 3
Один из вариантов для 3D-анимации (не OpenGL) для Android - реализовать анимацию в методе ViewGroup getChildStaticTransform
, используя graphics.Camera
и Matrix
.
В широком смысле это делается следующим образом:
-
Расширьте ViewGroup или его подкласс.
-
В конструкторах установите staticTransformationEnabled в true:
setStaticTransformationsEnabled(true);
-
Переопределить защищенный метод getChildStaticTransformation (View view, Transformation t).
-
В getChildStaticTransformation используйте graphics.Camera
для поворота View
в соответствии с вашим изображением.
-
Получите матрицу камеры и настройте ее, чтобы центрировать положение камеры на экране.
Например, так выглядит эффект 3d-перевода в 3d-карусель Игоря Кушнарева:
protected boolean getChildStaticTransformation(View child, Transformation transformation) {
//...
// Center of the item
float centerX = (float)child.getWidth()/2, centerY = (float)child.getHeight()/2;
// Save camera
mCamera.save();
// Translate the item to it coordinates
final Matrix matrix = transformation.getMatrix();
mCamera.translate(((CarouselImageView)child).getX(),
((CarouselImageView)child).getY(),
((CarouselImageView)child).getZ());
// Get the camera matric and position the item
mCamera.getMatrix(matrix);
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
// Restore camera
mCamera.restore();
return true;
}
Вот несколько примеров кода о том, как использовать graphics.Camera
и Matrix
в getChildStaticTransformation
:
-
ViewPager3d от Inovex. Этот проект интересен тем, что если вы запустите его как есть, 3D-анимация не будет гладкой (на Galaxy S2). Напоследок, если вы разделите его на анимацию без камеры/матрицы, но сохраните полученные 3D-эффекты getChildStaticTransformation с помощью камеры и матрицы, 3D-эффекты будут плавными.
-
CoverFlow Нил Дэвис.
Ответ 4
Я не могу полностью понять ур, но, по-моему,
Ключом к плавной прокрутке Rotate3dAnimation является сохранение основных потоков приложений (поток пользовательского интерфейса) без большой обработки. Убедитесь, что вы производите доступ к диску, доступ к сети или SQL-доступ в отдельном потоке.
Эта ссылка для списка просмотра Rotate3dAnimation...