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

  • 相关阅读:
    (Java) LeetCode 44. Wildcard Matching —— 通配符匹配
    (Java) LeetCode 30. Substring with Concatenation of All Words —— 与所有单词相关联的字串
    (Java) LeetCode 515. Find Largest Value in Each Tree Row —— 在每个树行中找最大值
    (Java) LeetCode 433. Minimum Genetic Mutation —— 最小基因变化
    (Java) LeetCode 413. Arithmetic Slices —— 等差数列划分
    (Java) LeetCode 289. Game of Life —— 生命游戏
    (Java) LeetCode 337. House Robber III —— 打家劫舍 III
    (Java) LeetCode 213. House Robber II —— 打家劫舍 II
    (Java) LeetCode 198. House Robber —— 打家劫舍
    (Java) LeetCode 152. Maximum Product Subarray —— 乘积最大子序列
  • 原文地址:https://www.cnblogs.com/exmyth/p/4975274.html
Copyright © 2011-2022 走看看