Подтвердить что ты не робот

Как сделать 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>
4b9b3361

Ответ 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 градусов, это проблема камеры. В этом случае мы должны поместить изображение дальше от зрителя.

  • И еще один трюк - иногда я устанавливаю паузу перед началом анимации:

    animation.setStartOffset(100);
    

Ответ 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...