13

在我的应用程序中,我使用Rotate3dAnimation 来显示 Google 地图。代码工作正常,但动画不流畅,旋转视图时也可以看到一些线条。请看一下我的代码并建议我如何使这个动画更流畅?高度赞赏以任何其他有效方式实现此类动画的建议。在此处输入图像描述

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>
4

4 回答 4

6

我只是给出一个小提示;但是现在我工作太忙了,我无法实现这一点。

步骤是

  • 获取你的绘图缓存位图
  • 仅使用此位图将您的内容设置为图像视图
  • 将动画应用到这个 imageview
  • 在动画结束时重新设置您的内容

我相信这将使性能最大化。

稍后我将尝试编写一些代码。

代码

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);
}
于 2012-06-15T14:47:00.610 回答
5

我做了这样的动画。我有同样的问题。所以,建议:

  • 使 xml 布局尽可能简单,您可以使用 android 中的 Hierarchy View 工具对其进行测试。它工具显示了建造和绘制老鹰的时间;

  • 布局上的图像应具有尽可能低的权重;

  • 如果您的设备支持(在清单中),请使用硬件加速:

  • 我注意到另一种有趣的行为。如果我在 onAnimationEnd(Animation animation) 方法中调用一些代码,动画会冻结一小段时间。我使用下一个构造解决了这个问题:

    私有静态最终 int DELAY_AFTER_ANIMATION = 10;

    public void onAnimationEnd(Animation animation) { new Handler().postDelayed(new Runnable() { @Override public void run() { setData(); // 在这里做繁重的代码 } }, 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);
    
于 2012-06-15T15:43:20.507 回答
2

Android 上(非 OpenGL)3d 动画效果的一种选择是使用和类在 ViewGroup 的getChildStaticTransform方法中实现动画。graphics.CameraMatrix

从广义上讲,它是这样完成的:

  • 扩展ViewGroup或其子类。

  • 在构造函数中,将 staticTransformationEnabled 设置为 true:

     setStaticTransformationsEnabled(true);
    
  • 覆盖受保护的方法 getChildStaticTransformation(View view, Transformation t)。

  • 在 getChildStaticTransformation 中,用于根据您的图片graphics.Camera旋转。View

  • 获取相机的矩阵并调整它以使相机位置在视图上居中。

例如,这是Igor Kushnarev在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's coordinates
    final Matrix matrix = transformation.getMatrix();
    mCamera.translate(((CarouselImageView)child).getX(), 
            ((CarouselImageView)child).getY(), 
            ((CarouselImageView)child).getZ());

    // Get the camera's matric and position the item
    mCamera.getMatrix(matrix);
    matrix.preTranslate(-centerX, -centerY);
    matrix.postTranslate(centerX, centerY);

    // Restore camera
    mCamera.restore();      

    return true;
 }    

以下是有关如何使用graphics.CameraMatrixin的更多代码示例getChildStaticTransformation

  • Inovex的 ViewPager3d。这个项目很有趣,因为如果按原样运行,3d 动画会不流畅(在 Galaxy S2 上)。悬停,如果你去掉非相机/矩阵过度滚动动画,但保持 getChildStaticTransformation 3d 效果与相机和矩阵一起完成,3d 效果是平滑的。

  • 尼尔戴维斯的封面流。

于 2012-06-20T05:41:47.297 回答
-1

我不能完全理解你的问题,但据我所知,

平滑滚动的 Rotate3dAnimation 的关键是使应用程序的主线程(UI 线程)免于繁重的处理。确保您在单独的线程中执行任何磁盘访问、网络访问或 SQL 访问。

链接适用于列表视图 Rotate3dAnimation...

于 2012-07-31T07:34:46.813 回答