Android自定义view教程04-------------自定义属性动画

不太会美工,所以随便写了个菜单打开关闭的动画,主要是教会大家如何使用属性动画,可以这么说

学会属性动画 前面的fream动画和tween动画可以不用看了,因为他们2能做的,属性动画也能做,

他们2不能做的,属性动画也能做。

技术分享

直接上代码吧,注释写的还算详细。

 

主activity代码 实际上没啥好看的,主要就是使用了dialogfragment,没有用dialog,因为谷歌后来推荐

我们使用这个dialogfragment,而且这个确实比dialog要优秀方便很多。

package com.example.dialogfragmenttest;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {

    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) this.findViewById(R.id.tv);
        tv.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                MyDialogFragment fragment = new MyDialogFragment();
                FragmentTransaction ft = getSupportFragmentManager()
                        .beginTransaction();
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                fragment.show(ft, "df");
            }
        });
    }

}

然后主要看一下我们这个dialogfragment的代码,这边代码写的比较乱,不过还算详细,我并没有怎么优化他,主要目的是让大家看懂。有兴趣的同学可以继续优化这边代码

  1 package com.example.dialogfragmenttest;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 import android.animation.Animator;
  7 import android.animation.AnimatorSet;
  8 import android.animation.ObjectAnimator;
  9 import android.os.Bundle;
 10 import android.os.Handler;
 11 import android.support.v4.app.DialogFragment;
 12 import android.view.LayoutInflater;
 13 import android.view.View;
 14 import android.view.ViewGroup;
 15 import android.view.WindowManager;
 16 import android.view.animation.LinearInterpolator;
 17 import android.widget.ImageView;
 18 import android.widget.TextView;
 19 
 20 import com.nineoldandroids.view.ViewHelper;
 21 
 22 public class MyDialogFragment extends DialogFragment {
 23 
 24     // 显示菜单的动画 集
 25     private AnimatorSet mAnimatorSetShowMenu;
 26 
 27     // 关闭菜单的动画 集
 28     private AnimatorSet mAnimatorSetCloseMenu;
 29 
 30     // 那个叉叉按钮 实际上就是关闭菜单用的
 31     private ImageView cancelImageView;
 32     // 这个是叉叉按钮旁边的文字 但是在xml文件里设置了不可见
 33     // 这么做的原因是 动画要配对,如果是6个图片对应着5个文字的话,动画就不配对了
 34     // 这个地方放这个不可见的tv 只是为了配对,不信大家把这个tv删掉就知道 动画效果很奇怪了
 35     private TextView icncancel_tv;
 36 
 37     // 这边定义的就是四个菜单 每个菜单分别有文字部分和图片部分
 38     private TextView oneTv, twoTv, threeTv, fourTv;
 39     private ImageView oneIv, twoIv, threeIv, fourIv;
 40 
 41     private Handler mHandler = new Handler() {
 42         public void handleMessage(android.os.Message msg) {
 43 
 44             switch (msg.what) {
 45             // 关闭菜单
 46             case 0:
 47                 mAnimatorSetCloseMenu.start();
 48                 break;
 49             // 打开菜单
 50             case 1:
 51                 mAnimatorSetShowMenu.start();
 52                 break;
 53             default:
 54                 break;
 55             }
 56         };
 57     };
 58 
 59     @Override
 60     public View onCreateView(LayoutInflater inflater, ViewGroup container,
 61             Bundle savedInstanceState) {
 62         View v = inflater.inflate(R.layout.fragment_menu, container, false);
 63         initViews(v);
 64         // 初始化打开菜单和关闭菜单的动画set
 65         initMenuOpenAnimatorSet();
 66         initMenuCloseAnimatorSet();
 67         // 设置一些属性
 68         getDialog().getWindow().clearFlags(
 69                 WindowManager.LayoutParams.FLAG_DIM_BEHIND);
 70         // 这个函数调用实际上很重要,因为我们这个对话框刚成立的时候 一定要把我们的view
 71         // 设置成不可见的,实际上反过来说就是一定要把我们的控件设置成关闭后的属性。
 72         // 不然效果就变成了一开始这些view 就直接刷出来了,然后刷出来以后再执行动画,
 73         // 那样效果很糟糕,可以尝试把这个代码删掉就知道是什么效果
 74         initStartStatus();
 75         // 打开菜单
 76         mHandler.sendEmptyMessage(1);
 77         return v;
 78     }
 79 
 80     @Override
 81     public void onCreate(Bundle savedInstanceState) {
 82         // TODO Auto-generated method stub
 83         super.onCreate(savedInstanceState);
 84         // 设置对话框属性
 85         setStyle(STYLE_NO_FRAME, R.style.MenuFragmentStyle);
 86 
 87     }
 88 
 89     private void initViews(View view) {
 90         cancelImageView = (ImageView) view.findViewById(R.id.cancel_icon);
 91         cancelImageView.setOnClickListener(new View.OnClickListener() {
 92 
 93             @Override
 94             public void onClick(View v) {
 95                 // TODO Auto-generated method stub
 96                 mHandler.sendEmptyMessage(0);
 97             }
 98         });
 99         icncancel_tv = (TextView) view.findViewById(R.id.icncancel_tv);
100         oneIv = (ImageView) view.findViewById(R.id.icn1);
101         oneTv = (TextView) view.findViewById(R.id.icn1_tv);
102 
103         twoIv = (ImageView) view.findViewById(R.id.icn2);
104         twoTv = (TextView) view.findViewById(R.id.icn2_tv);
105 
106         threeIv = (ImageView) view.findViewById(R.id.icn3);
107         threeTv = (TextView) view.findViewById(R.id.icn3_tv);
108 
109         fourIv = (ImageView) view.findViewById(R.id.icn4);
110         fourTv = (TextView) view.findViewById(R.id.icn4_tv);
111     }
112 
113     /**
114      * 初始化一开始的状态 其实也就是关闭菜单以后的状态 这里我们偷懒引用了nineoldandroids 这个库里面的函数
115      * 有兴趣的同学可以谷歌一下nineoldandroids这个库的用法和介绍
116      * 
117      */
118     private void initStartStatus() {
119         ViewHelper.setAlpha(oneTv, 0);
120         ViewHelper.setAlpha(twoTv, 0);
121         ViewHelper.setAlpha(threeTv, 0);
122         ViewHelper.setAlpha(fourTv, 0);
123 
124         ViewHelper.setRotation(cancelImageView, 0);
125         ViewHelper.setRotationY(cancelImageView, -90);
126         ViewHelper.setRotationX(cancelImageView, 0);
127 
128         ViewHelper.setRotation(oneIv, 0);
129         ViewHelper.setRotationY(oneIv, 0);
130         ViewHelper.setRotationX(oneIv, -90);
131         ViewHelper.setRotation(twoIv, 0);
132         ViewHelper.setRotationY(twoIv, 0);
133         ViewHelper.setRotationX(twoIv, -90);
134         ViewHelper.setRotation(threeIv, 0);
135         ViewHelper.setRotationY(threeIv, 0);
136         ViewHelper.setRotationX(threeIv, -90);
137         ViewHelper.setRotation(fourIv, 0);
138         ViewHelper.setRotationY(fourIv, 0);
139         ViewHelper.setRotationX(fourIv, -90);
140     }
141 
142     /**
143      * 获取关闭菜单的动画集合
144      */
145     private void initMenuCloseAnimatorSet() {
146 
147         // 文本部分的动画--关闭
148         List<Animator> textAnimations = new ArrayList<Animator>();
149         // 图片部分的动画--关闭
150         List<Animator> imageAnimations = new ArrayList<Animator>();
151 
152         // 下面开始弄 文本部分消失的时候的动画 实际上这里就是和打开菜单的动画是相反的
153         // 与打开动画相同的是 我们的icncancel_tv 这个控件依然是不可见的,只不过起到一个填充的顺序
154         // 保证动画一起执行时的顺序
155         AnimatorSet textAnimatorSetXX = new AnimatorSet();
156         textAnimatorSetXX.playTogether(
157                 ObjectAnimator.ofFloat(icncancel_tv, "alpha", 1, 0),
158                 ObjectAnimator.ofFloat(icncancel_tv, "translationX", 0, 8));
159         AnimatorSet textAnimatorSetOne = new AnimatorSet();
160         textAnimatorSetOne.playTogether(
161                 ObjectAnimator.ofFloat(oneTv, "alpha", 1, 0),
162                 ObjectAnimator.ofFloat(oneTv, "translationX", 0, 8));
163 
164         AnimatorSet textAnimatorSetTwo = new AnimatorSet();
165         textAnimatorSetTwo.playTogether(
166                 ObjectAnimator.ofFloat(twoTv, "alpha", 1, 0),
167                 ObjectAnimator.ofFloat(twoTv, "translationX", 0, 8));
168 
169         AnimatorSet textAnimatorSeThree = new AnimatorSet();
170         textAnimatorSeThree.playTogether(
171                 ObjectAnimator.ofFloat(threeTv, "alpha", 1, 0),
172                 ObjectAnimator.ofFloat(threeTv, "translationX", 0, 8));
173 
174         AnimatorSet textAnimatorSetFour = new AnimatorSet();
175         textAnimatorSetFour.playTogether(
176                 ObjectAnimator.ofFloat(fourTv, "alpha", 1, 0),
177                 ObjectAnimator.ofFloat(fourTv, "translationX", 0, 8));
178         // 把文字动画放在一起 注意顺序 注意这次是关闭的动画 看一下这个add的顺序
179         // 从下往上关闭菜单 所以add的顺序要注意
180         textAnimations.add(textAnimatorSetFour);
181         textAnimations.add(textAnimatorSeThree);
182         textAnimations.add(textAnimatorSetTwo);
183         textAnimations.add(textAnimatorSetOne);
184         textAnimations.add(textAnimatorSetXX);
185 
186         // 图片动画也是一样 从下往上关闭 所以要注意add的顺序
187         imageAnimations
188                 .add(ObjectAnimator.ofFloat(fourIv, "rotationX", 0, -90));
189         imageAnimations.add(ObjectAnimator
190                 .ofFloat(threeIv, "rotationX", 0, -90));
191         imageAnimations.add(ObjectAnimator.ofFloat(twoIv, "rotationX", 0, -90));
192         imageAnimations.add(ObjectAnimator.ofFloat(oneIv, "rotationX", 0, -90));
193         // 第一个叉叉出现时的动画 放入集合 注意他是从右往左出现的
194         imageAnimations.add(ObjectAnimator.ofFloat(cancelImageView,
195                 "rotationY", 0, -90));
196 
197         // 现在我们来获取动画的集合
198         // 先获取文字的动画集合
199         AnimatorSet textCloseAnimatorSet = new AnimatorSet();
200         textCloseAnimatorSet.playSequentially(textAnimations);
201         // 再获取图片的动画集合
202         AnimatorSet imageCloseAnimatorSet = new AnimatorSet();
203         imageCloseAnimatorSet.playSequentially(imageAnimations);
204 
205         // 最后把文字和图片的属性动画 放在一起
206         // 所以在这要保证 一定是6个图片动画和6个文字动画 不然就不是配对执行了,执行起来会比较难看
207         mAnimatorSetCloseMenu = new AnimatorSet();
208         mAnimatorSetCloseMenu.playTogether(imageCloseAnimatorSet,
209                 textCloseAnimatorSet);
210         mAnimatorSetCloseMenu.setDuration(300);
211         mAnimatorSetCloseMenu.setStartDelay(0);
212         // 插值器
213         mAnimatorSetCloseMenu.setInterpolator(new LinearInterpolator());
214 
215     }
216 
217     /**
218      * 获取打开菜单的动画集合
219      */
220     private void initMenuOpenAnimatorSet() {
221         // 文本部分的动画 注意这是一个list 这里面的元素每一个都是 单独的属性动画
222         List<Animator> textAnimations = new ArrayList<Animator>();
223         // 图片部分的动画
224         List<Animator> imageAnimations = new ArrayList<Animator>();
225         // 第一个叉叉出现时的动画 放入集合 注意他是从右往左出现的
226         imageAnimations.add(ObjectAnimator.ofFloat(cancelImageView,
227                 "rotationY", -90, 0));
228 
229         // 注意其他菜单元素出现的时候imageview 都是 从上往下出现的和第一个xx出现的时候不一样
230         imageAnimations.add(ObjectAnimator.ofFloat(oneIv, "rotationX", 90, 0));
231         imageAnimations.add(ObjectAnimator.ofFloat(twoIv, "rotationX", 90, 0));
232         imageAnimations.add(ObjectAnimator
233                 .ofFloat(threeIv, "rotationX", -90, 0));
234         imageAnimations
235                 .add(ObjectAnimator.ofFloat(fourIv, "rotationX", -90, 0));
236 
237         // 下面开始弄 文本部分出现的时候的动画
238         AnimatorSet textAnimatorSetXX = new AnimatorSet();
239         textAnimatorSetXX.playTogether(
240                 ObjectAnimator.ofFloat(icncancel_tv, "alpha", 0, 1),
241                 ObjectAnimator.ofFloat(icncancel_tv, "translationX", 8, 0));
242         // 文本动画其实比较简单 一个是透明度的变化 另外带着一点位移动画
243         AnimatorSet textAnimatorSetOne = new AnimatorSet();
244         // 前面是透明度变化 后面是位移变化
245         textAnimatorSetOne.playTogether(
246                 ObjectAnimator.ofFloat(oneTv, "alpha", 0, 1),
247                 ObjectAnimator.ofFloat(oneTv, "translationX", 8, 0));
248 
249         AnimatorSet textAnimatorSetTwo = new AnimatorSet();
250         // 前面是透明度变化 后面是位移变化
251         textAnimatorSetTwo.playTogether(
252                 ObjectAnimator.ofFloat(twoTv, "alpha", 0, 1),
253                 ObjectAnimator.ofFloat(twoTv, "translationX", 8, 0));
254 
255         AnimatorSet textAnimatorSeThree = new AnimatorSet();
256         // 前面是透明度变化 后面是位移变化
257         textAnimatorSeThree.playTogether(
258                 ObjectAnimator.ofFloat(threeTv, "alpha", 0, 1),
259                 ObjectAnimator.ofFloat(threeTv, "translationX", 8, 0));
260 
261         AnimatorSet textAnimatorSetFour = new AnimatorSet();
262         // 前面是透明度变化 后面是位移变化
263         textAnimatorSetFour.playTogether(
264                 ObjectAnimator.ofFloat(fourTv, "alpha", 0, 1),
265                 ObjectAnimator.ofFloat(fourTv, "translationX", 8, 0));
266         // 把文字动画放在一起 注意顺序
267         textAnimations.add(textAnimatorSetXX);
268         textAnimations.add(textAnimatorSetOne);
269         textAnimations.add(textAnimatorSetTwo);
270         textAnimations.add(textAnimatorSeThree);
271         textAnimations.add(textAnimatorSetFour);
272 
273         // 现在我们来获取动画的集合
274         // 先获取文字的动画集合
275         AnimatorSet textOpenAnimatorSet = new AnimatorSet();
276         textOpenAnimatorSet.playSequentially(textAnimations);
277         // 再获取图片的动画集合
278         AnimatorSet imageOpenAnimatorSet = new AnimatorSet();
279         imageOpenAnimatorSet.playSequentially(imageAnimations);
280 
281         // 最后把文字和图片的属性动画 放在一起
282         mAnimatorSetShowMenu = new AnimatorSet();
283         mAnimatorSetShowMenu.playTogether(imageOpenAnimatorSet,
284                 textOpenAnimatorSet);
285         mAnimatorSetShowMenu.setDuration(300);
286         mAnimatorSetShowMenu.setStartDelay(0);
287         // 插值器
288         mAnimatorSetShowMenu.setInterpolator(new LinearInterpolator());
289     }
290 }

然后把这个dialog的布局文件看一下,

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/menu_fragment_background"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp" >

        <ImageView
            android:id="@+id/cancel_icon"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:background="#ffffff"
            android:src="@drawable/icn_close" >
        </ImageView>

        <TextView
            android:id="@+id/icncancel_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp"
            android:layout_toLeftOf="@id/cancel_icon"
            android:gravity="center"
            android:text="发送短信"
            android:textColor="#ffffffff"
            android:textSize="14sp"
            android:visibility="invisible" >
        </TextView>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/icn1"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:background="#ffffff"
            android:src="@drawable/icn_1" >
        </ImageView>

        <TextView
            android:id="@+id/icn1_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp"
            android:layout_toLeftOf="@id/icn1"
            android:gravity="center"
            android:text="发送短信"
            android:textColor="#ffffffff"
            android:textSize="14sp" >
        </TextView>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/icn2"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:background="#ffffff"
            android:src="@drawable/icn_2" >
        </ImageView>

        <TextView
            android:id="@+id/icn2_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp"
            android:layout_toLeftOf="@id/icn2"
            android:gravity="center"
            android:text="点赞"
            android:textColor="#ffffffff"
            android:textSize="14sp" >
        </TextView>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/icn3"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:background="#ffffff"
            android:src="@drawable/icn_3" >
        </ImageView>

        <TextView
            android:id="@+id/icn3_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp"
            android:layout_toLeftOf="@id/icn3"
            android:gravity="center"
            android:text="添加联系人"
            android:textColor="#ffffffff"
            android:textSize="14sp" >
        </TextView>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <ImageView
            android:id="@+id/icn4"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_alignParentRight="true"
            android:background="#ffffff"
            android:src="@drawable/icn_4" >
        </ImageView>

        <TextView
            android:id="@+id/icn4_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp"
            android:layout_toLeftOf="@id/icn4"
            android:gravity="center"
            android:text="爱他就点他"
            android:textColor="#ffffffff"
            android:textSize="14sp" >
        </TextView>
    </RelativeLayout>

</LinearLayout>

基本就是这样了,有看不懂的可以私信问我,或者留言问。

这边偷懒了,没有继续设置PivotX PivotY这2个属性的 变化,否则应该动画效果更美丽一些,有兴趣的可以自行拓展。

 

关于Android动画的教程差不多就到这里结束了,下面应该会讲一下 自定义view的一些原理和技巧,然后讲一下滑动触摸事件,

 

最后3者结合起来带大家分析1-2个牛逼的开源控件,这个系列的教程就要结束了,争取4月份能结束他!

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。