zoukankan      html  css  js  c++  java
  • [转]Android UI:看看Google官方自定义带旋转动画的ImageView-----RotateImageView怎么写(附 图片淡入淡出效果)

    http://blog.csdn.net/yanzi1225627/article/details/22439119

    众所周知,想要让ImageView旋转的话,可以用setRotation()让其围绕中心点旋转,但这个旋转是不带动画的,也就是旋转屏幕时图片噌的一下就转过去了,看不到旋转的过程,此UI体验不大好,为此需要自定义带旋转动画的ImageView.虽然Google SDK里基本控件里没有,但在Camera的原生APP代码里却给出了带旋转动画的ImageView,即今天的主角:RotateImageView。

    尽管民间已有链接1 链接2  链接3 提供思路实现带旋转动画的ImageView,都不如Google官方标配的啊。先上源码吧,为实现此目的,需要四个文件:

    1、Rotatable.java

    1. <span style="font-family:Comic Sans MS;font-size:18px;">/* 
    2.  * Copyright (C) 2011 The Android Open Source Project 
    3.  * 
    4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
    5.  * you may not use this file except in compliance with the License. 
    6.  * You may obtain a copy of the License at 
    7.  * 
    8.  *      http://www.apache.org/licenses/LICENSE-2.0 
    9.  * 
    10.  * Unless required by applicable law or agreed to in writing, software 
    11.  * distributed under the License is distributed on an "AS IS" BASIS, 
    12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    13.  * See the License for the specific language governing permissions and 
    14.  * limitations under the License. 
    15.  */  
    16.   
    17. package com.android.ui;  
    18.   
    19. public interface Rotatable {  
    20.     // Set parameter 'animation' to true to have animation when rotation.  
    21.     public void setOrientation(int orientation, boolean animation);  
    22. }  
    23. </span>  


    他就是个接口,里面有setOrientation这个方法。Google这么写是因为有大量自定义UI都要继承这个接口。

    2、TwoStateImageView.java

    1. <span style="font-family:Comic Sans MS;font-size:18px;">/* 
    2.  * Copyright (C) 2011 The Android Open Source Project 
    3.  * 
    4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
    5.  * you may not use this file except in compliance with the License. 
    6.  * You may obtain a copy of the License at 
    7.  * 
    8.  *      http://www.apache.org/licenses/LICENSE-2.0 
    9.  * 
    10.  * Unless required by applicable law or agreed to in writing, software 
    11.  * distributed under the License is distributed on an "AS IS" BASIS, 
    12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    13.  * See the License for the specific language governing permissions and 
    14.  * limitations under the License. 
    15.  */  
    16.   
    17. package com.android.ui;  
    18.   
    19. import android.content.Context;  
    20. import android.util.AttributeSet;  
    21. import android.widget.ImageView;  
    22.   
    23. /** 
    24.  * A @{code ImageView} which change the opacity of the icon if disabled. 
    25.  */  
    26. public class TwoStateImageView extends ImageView {  
    27.     private static final int ENABLED_ALPHA = 255;  
    28.     private static final int DISABLED_ALPHA = (int) (255 * 0.4);  
    29.     private boolean mFilterEnabled = true;  
    30.   
    31.     public TwoStateImageView(Context context, AttributeSet attrs) {  
    32.         super(context, attrs);  
    33.     }  
    34.   
    35.     public TwoStateImageView(Context context) {  
    36.         this(context, null);  
    37.     }  
    38.   
    39.     @SuppressWarnings("deprecation")  
    40.     @Override  
    41.     public void setEnabled(boolean enabled) {  
    42.         super.setEnabled(enabled);  
    43.         if (mFilterEnabled) {  
    44.             if (enabled) {  
    45.                 setAlpha(ENABLED_ALPHA);  
    46.             } else {  
    47.                 setAlpha(DISABLED_ALPHA);  
    48.             }  
    49.         }  
    50.     }  
    51.   
    52.     public void enableFilter(boolean enabled) {  
    53.         mFilterEnabled = enabled;  
    54.     }  
    55. }  
    56. </span>  


    在ImageView的基础上增加了mFilterEnabled这个属性,开关打开后,通过改变图片的Alpha实现两种状态,默认这个开关是开的,图片透明度为255,即不透明。

    3、RotateImageView.java

    1. <span style="font-family:Comic Sans MS;font-size:18px;">/* 
    2.  * Copyright (C) 2009 The Android Open Source Project 
    3.  * 
    4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
    5.  * you may not use this file except in compliance with the License. 
    6.  * You may obtain a copy of the License at 
    7.  * 
    8.  *      http://www.apache.org/licenses/LICENSE-2.0 
    9.  * 
    10.  * Unless required by applicable law or agreed to in writing, software 
    11.  * distributed under the License is distributed on an "AS IS" BASIS, 
    12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    13.  * See the License for the specific language governing permissions and 
    14.  * limitations under the License. 
    15.  */  
    16.   
    17. package com.android.ui;  
    18.   
    19. import android.content.Context;  
    20. import android.graphics.Bitmap;  
    21. import android.graphics.Canvas;  
    22. import android.graphics.Rect;  
    23. import android.graphics.drawable.BitmapDrawable;  
    24. import android.graphics.drawable.Drawable;  
    25. import android.graphics.drawable.TransitionDrawable;  
    26. import android.media.ThumbnailUtils;  
    27. import android.util.AttributeSet;  
    28. import android.util.Log;  
    29. import android.view.ViewGroup.LayoutParams;  
    30. import android.view.animation.AnimationUtils;  
    31. import android.widget.ImageView;  
    32.   
    33. /** 
    34.  * A @{code ImageView} which can rotate it's content. 
    35.  */  
    36. public class RotateImageView extends TwoStateImageView implements Rotatable {  
    37.   
    38.     @SuppressWarnings("unused")  
    39.     private static final String TAG = "RotateImageView";  
    40.   
    41.     private static final int ANIMATION_SPEED = 270; // 270 deg/sec  
    42.   
    43.     private int mCurrentDegree = 0; // [0, 359]  
    44.     private int mStartDegree = 0;  
    45.     private int mTargetDegree = 0;  
    46.   
    47.     private boolean mClockwise = false, mEnableAnimation = true;  
    48.   
    49.     private long mAnimationStartTime = 0;  
    50.     private long mAnimationEndTime = 0;  
    51.   
    52.     public RotateImageView(Context context, AttributeSet attrs) {  
    53.         super(context, attrs);  
    54.     }  
    55.   
    56.     public RotateImageView(Context context) {  
    57.         super(context);  
    58.     }  
    59.   
    60.     protected int getDegree() {  
    61.         return mTargetDegree;  
    62.     }  
    63.   
    64.     // Rotate the view counter-clockwise  
    65.     @Override  
    66.     public void setOrientation(int degree, boolean animation) {  
    67.         mEnableAnimation = animation;  
    68.         // make sure in the range of [0, 359]  
    69.         degree = degree >= 0 ? degree % 360 : degree % 360 + 360;  
    70.         if (degree == mTargetDegree) return;  
    71.   
    72.         mTargetDegree = degree;  
    73.         if (mEnableAnimation) {  
    74.             mStartDegree = mCurrentDegree;  
    75.             mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis();  
    76.   
    77.             int diff = mTargetDegree - mCurrentDegree;  
    78.             diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]  
    79.   
    80.             // Make it in range [-179, 180]. That's the shorted distance between the  
    81.             // two angles  
    82.             diff = diff > 180 ? diff - 360 : diff;  
    83.   
    84.             mClockwise = diff >= 0;  
    85.             mAnimationEndTime = mAnimationStartTime  
    86.                     + Math.abs(diff) * 1000 / ANIMATION_SPEED;  
    87.         } else {  
    88.             mCurrentDegree = mTargetDegree;  
    89.         }  
    90.   
    91.         invalidate();  
    92.     }  
    93.   
    94.     @Override  
    95.     protected void onDraw(Canvas canvas) {  
    96.         Drawable drawable = getDrawable();  
    97.         if (drawable == null) return;  
    98.   
    99.         Rect bounds = drawable.getBounds();  
    100.         int w = bounds.right - bounds.left;  
    101.         int h = bounds.bottom - bounds.top;  
    102.   
    103.         if (w == 0 || h == 0) return; // nothing to draw  
    104.   
    105.         if (mCurrentDegree != mTargetDegree) {  
    106.             long time = AnimationUtils.currentAnimationTimeMillis();  
    107.             if (time < mAnimationEndTime) {  
    108.                 int deltaTime = (int)(time - mAnimationStartTime);  
    109.                 int degree = mStartDegree + ANIMATION_SPEED  
    110.                         * (mClockwise ? deltaTime : -deltaTime) / 1000;  
    111.                 degree = degree >= 0 ? degree % 360 : degree % 360 + 360;  
    112.                 mCurrentDegree = degree;  
    113.                 invalidate();  
    114.             } else {  
    115.                 mCurrentDegree = mTargetDegree;  
    116.             }  
    117.         }  
    118.   
    119.         int left = getPaddingLeft();  
    120.         int top = getPaddingTop();  
    121.         int right = getPaddingRight();  
    122.         int bottom = getPaddingBottom();  
    123.         int width = getWidth() - left - right;  
    124.         int height = getHeight() - top - bottom;  
    125.   
    126.         int saveCount = canvas.getSaveCount();  
    127.   
    128.         // Scale down the image first if required.  
    129.         if ((getScaleType() == ImageView.ScaleType.FIT_CENTER) &&  
    130.                 ((width < w) || (height < h))) {  
    131.             float ratio = Math.min((float) width / w, (float) height / h);  
    132.             canvas.scale(ratio, ratio, width / 2.0f, height / 2.0f);  
    133.         }  
    134.         canvas.translate(left + width / 2, top + height / 2);  
    135.         canvas.rotate(-mCurrentDegree);  
    136.         canvas.translate(-w / 2, -h / 2);  
    137.         drawable.draw(canvas);  
    138.         canvas.restoreToCount(saveCount);  
    139.     }  
    140.   
    141.     private Bitmap mThumb;  
    142.     private Drawable[] mThumbs;  
    143.     private TransitionDrawable mThumbTransition;  
    144.   
    145.     public void setBitmap(Bitmap bitmap) {  
    146.         // Make sure uri and original are consistently both null or both  
    147.         // non-null.  
    148.         if (bitmap == null) {  
    149.             mThumb = null;  
    150.             mThumbs = null;  
    151.             setImageDrawable(null);  
    152.             setVisibility(GONE);  
    153.             return;  
    154.         }  
    155.   
    156.         LayoutParams param = getLayoutParams();  
    157.         //下面四行代码被我注释掉了,换成了固定值400*400 by yanguoqi 2014-3-28  
    158. //        final int miniThumbWidth = param.width  
    159. //                - getPaddingLeft() - getPaddingRight();  
    160. //        final int miniThumbHeight = param.height  
    161. //                - getPaddingTop() - getPaddingBottom();  
    162.         final int miniThumbWidth = 400;  
    163.         final int miniThumbHeight = 400;  
    164.           
    165.         Log.i("yan", "param.width = " + param.width + " getPaddingLeft() = "  
    166.                 + getPaddingLeft() + " getPaddingRight()" + getPaddingRight());  
    167.         Log.i("yan", "miniThumbWidth = " + miniThumbWidth);  
    168.         mThumb = ThumbnailUtils.extractThumbnail(  
    169.                 bitmap, miniThumbWidth, miniThumbHeight);  
    170.         Drawable drawable;  
    171.         if (mThumbs == null || !mEnableAnimation) {  
    172.             mThumbs = new Drawable[2];  
    173.             mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb);  
    174.             setImageDrawable(mThumbs[1]);  
    175.         } else {  
    176.             mThumbs[0] = mThumbs[1];  
    177.             mThumbs[1] = new BitmapDrawable(getContext().getResources(), mThumb);  
    178.             mThumbTransition = new TransitionDrawable(mThumbs);  
    179.             setImageDrawable(mThumbTransition);  
    180.             mThumbTransition.startTransition(500);  
    181.         }  
    182.         setVisibility(VISIBLE);  
    183.     }  
    184. }  
    185. </span>  


    整体没啥可说的,在setBitmap处有四句代码运行不正确我给换成了固定值。这个setBitmap干啥呢?是为了实现在同一个ImageView切换图片时的淡入淡出效果,如果单纯是旋转则不需要这个函数。不过本文的测试代码还是对这一功能做了测试。其思想也很简单,用Drawable[] mThumbs来存两个缩略图,第一次set的时候缩略图存一张,第二次再set的时候再放数组里一张,然后将Drawable[]数组实例化到TransitionDrawable变量里,通过这个变量的startTransition()显示淡入淡出效果,里面的参数表示时间。如果设成1000毫秒即1秒则会非常明显。关于TransitionDrawable的更多用法和解释可以参见 这里

    4、有了以上三个文件其实已经可以完成旋转ImageView了,在布局里定义成RotateImageView即可。但仍需要角度。下面这个函数是将连续的旋转角度0---360度变换成0°、90°、180°、270°四个值。我们旋转屏幕时,当成一定角度时才旋转图片,而不是稍微动一下就旋转,除非需求如此。

    Util.java

    1. <span style="font-family:Comic Sans MS;font-size:18px;">package com.android.util;  
    2.   
    3. import android.app.Activity;  
    4. import android.view.OrientationEventListener;  
    5. import android.view.Surface;  
    6.   
    7. public class Util {  
    8.     public static final int ORIENTATION_HYSTERESIS = 5;  
    9.   
    10.     public static int roundOrientation(int orientation, int orientationHistory) {  
    11.         boolean changeOrientation = false;  
    12.         if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {  
    13.             changeOrientation = true;  
    14.         } else {  
    15.             int dist = Math.abs(orientation - orientationHistory);  
    16.             dist = Math.min( dist, 360 - dist );  
    17.             changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS );  
    18.         }  
    19.         if (changeOrientation) {  
    20.             return ((orientation + 45) / 90 * 90) % 360;  
    21.         }  
    22.         return orientationHistory;  
    23.     }  
    24.     public static int getDisplayRotation(Activity activity) {  
    25.         int rotation = activity.getWindowManager().getDefaultDisplay()  
    26.                 .getRotation();  
    27.         switch (rotation) {  
    28.             case Surface.ROTATION_0: return 0;  
    29.             case Surface.ROTATION_90: return 90;  
    30.             case Surface.ROTATION_180: return 180;  
    31.             case Surface.ROTATION_270: return 270;  
    32.         }  
    33.         return 0;  
    34.     }  
    35. }  
    36. </span>  


    下面就要解决如何获得屏幕旋转角度的问题。最初我也想着用onConfigurationChanged()但发现这就是扯淡,这个只能检测此时处在横屏还是竖屏。后面再交代其用法。最终是用OrientationEventListener监测的。

    MainActivity.java代码如下:

    1. <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.testrotateimageview;  
    2.   
    3. import android.app.Activity;  
    4. import android.content.Context;  
    5. import android.content.res.Configuration;  
    6. import android.graphics.Bitmap;  
    7. import android.graphics.BitmapFactory;  
    8. import android.os.Bundle;  
    9. import android.util.Log;  
    10. import android.view.Menu;  
    11. import android.view.OrientationEventListener;  
    12. import android.view.View;  
    13. import android.widget.Button;  
    14. import android.widget.ImageView;  
    15.   
    16. import com.android.ui.RotateImageView;  
    17. import com.android.util.Util;  
    18.   
    19. public class MainActivity extends Activity {  
    20.     private static final String tag = "yan";  
    21.     RotateImageView rotateImg1;  
    22.     RotateImageView rotateImg2;  
    23.     ImageView commonImg;  
    24.     Button fadeBtn;  
    25.     MyOrientationEventListener mOrientationListener;  
    26.     Bitmap a;  
    27.     Bitmap b;  
    28.     boolean flag = true;  
    29.     int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;  
    30.     @Override  
    31.     protected void onCreate(Bundle savedInstanceState) {  
    32.         super.onCreate(savedInstanceState);  
    33.         setContentView(R.layout.activity_main);  
    34.         initUI();  
    35.         mOrientationListener = new MyOrientationEventListener(this);  
    36.         b = BitmapFactory.decodeResource(getResources(), R.drawable.kunqing2);  
    37.         a = BitmapFactory.decodeResource(getResources(), R.drawable.kunlong);  
    38.         fadeBtn.setOnClickListener(new View.OnClickListener() {  
    39.   
    40.             @Override  
    41.             public void onClick(View v) {  
    42.                 // TODO Auto-generated method stub  
    43.                 if(flag){  
    44.                     rotateImg1.setBitmap(b);  
    45.                     flag = false;  
    46.                 }  
    47.                 else{  
    48.                     rotateImg1.setBitmap(a);  
    49.                     flag = true;  
    50.                 }  
    51.             }  
    52.         });  
    53.   
    54.   
    55.   
    56.   
    57.   
    58.     }  
    59.   
    60.     @Override  
    61.     public boolean onCreateOptionsMenu(Menu menu) {  
    62.         // Inflate the menu; this adds items to the action bar if it is present.  
    63.         getMenuInflater().inflate(R.menu.main, menu);  
    64.         return true;  
    65.     }  
    66.   
    67.   
    68.     @Override  
    69.     protected void onResume() {  
    70.         // TODO Auto-generated method stub  
    71.         super.onResume();  
    72.         mOrientationListener.enable();  
    73.     }  
    74.   
    75.     @Override  
    76.     protected void onPause() {  
    77.         // TODO Auto-generated method stub  
    78.         super.onPause();  
    79.         mOrientationListener.disable();  
    80.     }  
    81.   
    82.     private void initUI(){  
    83.         rotateImg1 = (RotateImageView)findViewById(R.id.rotate_img_1);  
    84.         rotateImg1.setImageResource(R.drawable.nan_1);  
    85.         rotateImg2 = (RotateImageView)findViewById(R.id.rotate_img_2);  
    86.         rotateImg2.setImageResource(R.drawable.nan_2);  
    87.         commonImg = (ImageView)findViewById(R.id.common_img);  
    88.         fadeBtn = (Button)findViewById(R.id.btn_fade);  
    89.     }  
    90.     private class MyOrientationEventListener extends OrientationEventListener{  
    91.   
    92.         public MyOrientationEventListener(Context context) {  
    93.             super(context);  
    94.             // TODO Auto-generated constructor stub  
    95.         }  
    96.   
    97.         @Override  
    98.         public void onOrientationChanged(int orientation) {  
    99.             // TODO Auto-generated method stub  
    100.             if(orientation == OrientationEventListener.ORIENTATION_UNKNOWN){  
    101.                 return;  
    102.             }  
    103.             mOrientation = Util.roundOrientation(orientation, mOrientation);  
    104.             Log.i(tag, "MyOrientationEventListener mOrientation = " + mOrientation);  
    105.   
    106.             rotateImg1.setOrientation(mOrientation, true);  
    107.             rotateImg2.setOrientation(mOrientation, true);  
    108.             commonImg.setRotation(-mOrientation);  
    109.         }  
    110.   
    111.     }  
    112.     @Override  
    113.     public void onConfigurationChanged(Configuration newConfig) {  
    114.         // TODO Auto-generated method stub  
    115.         super.onConfigurationChanged(newConfig);  
    116.         int degree = newConfig.orientation;  
    117.         Log.i("yan", "onConfigurationChanged = " + degree);  
    118.     }  
    119.       
    120.   
    121. }  
    122. </span>  


    布局如下:activity_main.xml

    1. <span style="font-family:Comic Sans MS;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     android:paddingBottom="@dimen/activity_vertical_margin"  
    6.     android:paddingLeft="@dimen/activity_horizontal_margin"  
    7.     android:paddingRight="@dimen/activity_horizontal_margin"  
    8.     android:paddingTop="@dimen/activity_vertical_margin"  
    9.     tools:context=".MainActivity" >  
    10.   
    11.     <Button  
    12.         android:id="@+id/btn_fade"  
    13.         android:layout_width="wrap_content"  
    14.         android:layout_height="wrap_content"  
    15.         android:text="淡入淡出 效果测试" />  
    16.     <com.android.ui.RotateImageView   
    17.         android:id="@+id/rotate_img_1"  
    18.         android:layout_width="wrap_content"  
    19.         android:layout_height="wrap_content"  
    20.         android:layout_alignParentTop="true"  
    21.         android:layout_centerHorizontal="true"  
    22. />  
    23.     <com.android.ui.RotateImageView   
    24.         android:id="@+id/rotate_img_2"  
    25.         android:layout_width="wrap_content"  
    26.         android:layout_height="wrap_content"  
    27.         android:layout_marginTop="20dip"  
    28.         android:layout_below="@id/rotate_img_1"  
    29.         android:layout_centerHorizontal="true"/>  
    30.     <ImageView   
    31.         android:id="@+id/common_img"  
    32.          android:layout_width="wrap_content"  
    33.         android:layout_height="wrap_content"  
    34.           android:layout_below="@id/rotate_img_2"  
    35.           android:layout_marginTop="20dip"  
    36.         android:layout_centerHorizontal="true"  
    37.         android:src="@drawable/nan_1"/>  
    38.   
    39. </RelativeLayout>  
    40. </span>  


    运行效果: 下图是初始界面,三幅图,前两个是RotateImageView,第三个是一般的ImageView.可以看出当RoteteImageView设置不使用动画时,其旋转效果和ImageView的setRotation是一样的。第一幅图和第二图的差别,第一图南怀瑾先生的,四周不带透明区域,第二幅图我用ps做了四周的透明处理。

    如果不使用文中的Util.roundOrientation()函数,即有个角度就让它转,如果它的四周没有透明区域的话将会看到下图:

    (抱歉,截图不是一次截的,但效果是真实的,此图周四晚截得)

    下面这幅图是用大中兴的geek牛逼的连续拍照拍下来的,记录了四周不带透明区域旋转时图片变形的场景:

    第一副图片里的淡入淡出测试按钮大家自己按看效果,太晚了不传图了。

    代码下载:http://download.csdn.net/detail/yanzi1225627/7115009

  • 相关阅读:
    为什么要用MarkDown?
    Android Studio: Application Installation Failed
    git查看某个文件修改历史
    有些事现在不做,一辈子都不会做了
    onMouseOver&onMouseOut vs onMouseEnter&onMouseLeave
    versionCode & versionName
    display:none vs visibility:hidden
    polyfill
    combineReducers
    React Context
  • 原文地址:https://www.cnblogs.com/exmyth/p/4975274.html
Copyright © 2011-2022 走看看