一步一步实现自定义控件(二)
来源:互联网 发布:金山 阿里 腾讯 视频云 编辑:程序博客网 时间:2024/05/16 02:58
前言
接着上一篇文章,上一篇文章主要是简单介绍了下自定义控件分类,同时也把几个案例的效果图给贴了出来,接下里的几篇都是围绕那几个案例来了,详细介绍每个案例的实现,好了,开始我们的第一个案例吧
效果图
具体实现
分析
好了,我们来慢慢分析它:
首先看布局,应该是分三部分:外,中,内
然后看效果,应该是有三个点击事件:中间的Menu,里面的House,还有就是Android的Menu键.点击之后执行什么操作呢?仔细想想应该是执行了动画效果.
既然大框有了,我们就来慢慢实现我们刚才分析的
布局
布局我觉得就不用说了,应该很简单,就直接把代码贴出来了,完全是考验你相对布局的使用
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.hfs.youkumenudemo.MainActivity"> <RelativeLayout android:id="@+id/rl_level1" android:layout_width="100dp" android:layout_height="50dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@mipmap/level1"> <ImageButton android:id="@+id/ib_home" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:background="@null" android:src="@mipmap/icon_home"/> </RelativeLayout> <RelativeLayout android:id="@+id/rl_level2" android:layout_width="180dp" android:layout_height="90dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@mipmap/level2"> <ImageButton android:id="@+id/ib_menu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@null" android:src="@mipmap/icon_menu"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:background="@null" android:src="@mipmap/icon_search"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" android:background="@null" android:src="@mipmap/icon_myyouku"/> </RelativeLayout> <RelativeLayout android:id="@+id/rl_level3" android:layout_width="280dp" android:layout_height="140dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:background="@mipmap/level3"> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:layout_marginLeft="5dp" android:background="@null" android:src="@mipmap/channel1"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="30dp" android:layout_marginTop="50dp" android:background="@null" android:src="@mipmap/channel2"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="60dp" android:layout_marginTop="15dp" android:background="@null" android:src="@mipmap/channel3"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@null" android:src="@mipmap/channel4"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="60dp" android:layout_marginTop="15dp" android:background="@null" android:src="@mipmap/channel5"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="25dp" android:layout_marginTop="50dp" android:background="@null" android:src="@mipmap/channel6"/> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentBottom="true" android:layout_marginRight="5dp" android:background="@null" android:src="@mipmap/channel7"/> </RelativeLayout></RelativeLayout>
代码
终于到了代码部分了,我们还是一点点分析,一点点实现
首先我们在活头看一个这个效果图,我们看看我们都需要找到哪些控件的ID.我们可以看到,点击中间的menu键和点击里面的Home键,还有就是Android的Menu键都会触发动画效果,你点击之后,让某个控件执行动画,你是不是也得把这个控件的id找到,所以我们一共需要找到5个控件的id,同时加上点击事件
package com.example.hfs.youkumenudemo;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.ImageButton;import android.widget.RelativeLayout;public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; private RelativeLayout mRelativeLayout1; private RelativeLayout mRelativeLayout2; private RelativeLayout mRelativeLayout3; private ImageButton mButton1; private ImageButton mButton2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mButton1= (ImageButton) findViewById(R.id.ib_home); mButton1.setOnClickListener(this); mButton2= (ImageButton) findViewById(R.id.ib_menu); mButton2.setOnClickListener(this); mRelativeLayout1 = (RelativeLayout) this.findViewById(R.id.rl_level1); mRelativeLayout2 = (RelativeLayout) this.findViewById(R.id.rl_level2); mRelativeLayout3 = (RelativeLayout) this.findViewById(R.id.rl_level3); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.ib_home: break; case R.id.ib_menu: break; } }}
OK,接下来我们再看啊,当我点击中间的Menu菜单时,外面的那层是转出去的,但是我再点击的时候它又转过来了,对吧,所以我们要给它加一个boolean类型的标志位,同样的道理,其它两层也要加标志位,默认都是true,这样当我们点击的时候我们就要先判断下这个标志位了,一会再用这几个标志位
private boolean isLevel3Display=true; private boolean isLevel2Display=true; private boolean isLevel1Display=true;
接下来我们来看下这个动画,有两个,一个是”出”的动画,一个是”进”的动画.他们基本上是一样的,只不过就是方向相反,学过动画的估计都能猜出来,这应该是个补间动画中的旋转动画(推荐属性动画,这里使用补间动画一定要注意一个问题,下面我会说这个问题),好了,既然已经知道是哪种动画了,那就写代码实现它吧
package com.example.hfs.youkumenudemo.util;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.RelativeLayout;/** * Created by HFS on 2016/9/18. */public class AnimationUtils { /** * 旋转出去动画 */ public static void rotateOutAnim(RelativeLayout relativeLayout) { RotateAnimation rotateAnimation = new RotateAnimation( 0f, -180f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); relativeLayout.startAnimation(rotateAnimation); } public static void rotateInAnim(RelativeLayout relativeLayout,long delay) { RotateAnimation rotateAnimation = new RotateAnimation( -180f, 0f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); relativeLayout.startAnimation(rotateAnimation); }
解释下其中的一句代码:
rotateAnimation.setFillAfter(true);
补间动画必须要设置这个属性,表示停留在动画结束的位置
OK,有了动画以后,我们就可以吧我们之前的点击事件完善了
switch (view.getId()) { case R.id.ib_home: if (isLevel2Display){ if (isLevel3Display){ AnimationUtils.rotateOutAnim(mRelativeLayout3); isLevel3Display=false; } AnimationUtils.rotateOutAnim(mRelativeLayout2); isLevel2Display=false; }else{ AnimationUtils.rotateInAnim(mRelativeLayout2); isLevel2Display=true; } break; case R.id.ib_menu: //如果当前三级菜单显示,转出去 if (isLevel3Display){ AnimationUtils.rotateOutAnim(mRelativeLayout3); isLevel3Display=false; }else{ AnimationUtils.rotateInAnim(mRelativeLayout3); isLevel3Display=true; } break; }
好了,我们在看下效果图,我们发现,当我们点击里面的Home键时,外边的两层并不是同时执行的动画的,中间的这层会有一个延时,之后再执行动画,所以我们要修改下我们的动画工具类,把延时加上
package com.example.hfs.youkumenudemo.util;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.RelativeLayout;/** * Created by HFS on 2016/9/18. */public class AnimationUtils { /** * 旋转出去动画 */ public static void rotateOutAnim(RelativeLayout relativeLayout,long delay) { RotateAnimation rotateAnimation = new RotateAnimation( 0f, -180f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); rotateAnimation.setStartOffset(delay); relativeLayout.startAnimation(rotateAnimation); } public static void rotateInAnim(RelativeLayout relativeLayout,long delay) { RotateAnimation rotateAnimation = new RotateAnimation( -180f, 0f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); rotateAnimation.setStartOffset(delay); relativeLayout.startAnimation(rotateAnimation); }}
在实际操作过程找那个,我们发现,我们在点击中间那层的Menu的时候,我们会发现,转出动画没执行完,转进动画就开始了.而我们要实现的效果是,之后执行完转出动画,转入动画才会执行,这样就会保证接麦呢不会出现闪动的情况,怎么解决呢?
这样,我们记录下当前正在执行动画的个数,只要这个个数大于0,我就让相应控件的点击事件不生效.这样的话我们就需要对动画进行监听,动画开始,让这个个数加一,结束减一,好了,看下具体的代码
public static int runningAnimationCount=0; static class MyAnimationListener implements Animation.AnimationListener{ @Override public void onAnimationStart(Animation animation) { runningAnimationCount++; } @Override public void onAnimationEnd(Animation animation) { runningAnimationCount--; } @Override public void onAnimationRepeat(Animation animation) { } }
同时我们要给响应的动画设置监听
rotateAnimation.setAnimationListener(new MyAnimationListener());
同时还要在点击事件中添加一个判断:
@Override public void onClick(View view) { if (AnimationUtils.runningAnimationCount>0){ //当前又动画正在执行,不执行点击事件 return; } //...下面代码一样
OK这个问题解决了之后,我们还差一个监听Android的Menu键,这个只需要重写onKeyDown这个方法即可
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { // keyCode 事件码 System.out.println("onKeyDown: " + keyCode); if(keyCode == KeyEvent.KEYCODE_MENU){ if(AnimationUtils.runningAnimationCount > 0){ // 当前有动画正在执行, 取消当前事件 return true; }// 如果按下的是菜单按钮 if(isLevel1Display){ long delay = 0; // 隐藏三级菜单 if(isLevel3Display){ AnimationUtils.rotateOutAnim(mRelativeLayout3, 0); isLevel3Display = false; delay += 200; } // 隐藏二级菜单 if(isLevel2Display){ AnimationUtils.rotateOutAnim(mRelativeLayout2, delay); isLevel2Display = false; delay += 200; } // 隐藏一级菜单 AnimationUtils.rotateOutAnim(mRelativeLayout1, delay); }else { // 顺次转进来 AnimationUtils.rotateInAnim(mRelativeLayout1, 0); AnimationUtils.rotateInAnim(mRelativeLayout2, 200); AnimationUtils.rotateInAnim(mRelativeLayout3, 400); isLevel3Display = true; isLevel2Display = true; } isLevel1Display = !isLevel1Display; return true;// 消费了当前事件 } return super.onKeyDown(keyCode, event); }
接下来就是我上面提到的那个问题了,看下效果
发现问题了吧,当我们点击中间空吧的时候,依然会触发事件,这就是我们补间动画的缺点了.补间动画的缺点就是执行完动画,它的真实位置还是在原来的位置,但是属性动画就不这样,所以推荐使用属性动画
那怎么解决呢?
我们可以这样,就是当我们执行玩动画以后,我们让该控件的子控件都失去点击功能
int count = relativeLayout.getChildCount(); for (int i = 0; i < count; i++) { relativeLayout.getChildAt(i).setEnabled(false); }
这段代码加载我们的动画工具类里就可以了,下面时完整的动画工具类
package com.example.hfs.youkumenudemo.util;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.RelativeLayout;/** * Created by HFS on 2016/9/18. */public class AnimationUtils { /** * 旋转出去动画 */ public static void rotateOutAnim(RelativeLayout relativeLayout,long delay) { int count = relativeLayout.getChildCount(); for (int i = 0; i < count; i++) { relativeLayout.getChildAt(i).setEnabled(false); } RotateAnimation rotateAnimation = new RotateAnimation( 0f, -180f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); rotateAnimation.setStartOffset(delay); rotateAnimation.setAnimationListener(new MyAnimationListener()); relativeLayout.startAnimation(rotateAnimation); } public static void rotateInAnim(RelativeLayout relativeLayout,long delay) { int count = relativeLayout.getChildCount(); for (int i = 0; i < count; i++) { relativeLayout.getChildAt(i).setEnabled(true); } RotateAnimation rotateAnimation = new RotateAnimation( -180f, 0f, //起始角度 Animation.RELATIVE_TO_SELF, 0.5f,//相对坐标点 X值 Animation.RELATIVE_TO_SELF, 1f);//相对坐标点 Y值 rotateAnimation.setDuration(500); rotateAnimation.setFillAfter(true); rotateAnimation.setStartOffset(delay); rotateAnimation.setAnimationListener(new MyAnimationListener()); relativeLayout.startAnimation(rotateAnimation); } public static int runningAnimationCount=0; static class MyAnimationListener implements Animation.AnimationListener{ @Override public void onAnimationStart(Animation animation) { runningAnimationCount++; } @Override public void onAnimationEnd(Animation animation) { runningAnimationCount--; } @Override public void onAnimationRepeat(Animation animation) { } }}
Ok这样我们的第一个案例就完成了
项目地址:
https://github.com/Greathfs/YoukuMenuDemo
- 一步一步实现自定义控件(二)
- 一步一步实现自定义控件(一)
- 一步一步实现自定义控件(三)
- 一步一步实现自定义控件(四)
- 一步一步走向自定义控件
- C#自定义控件一步一步走
- 自定义控件<二> 通讯录的简单实现
- 自定义View实例(二)----一步一步教你实现QQ健康界面
- 自定义View实例(二)----一步一步教你实现QQ健康界面
- 一步一步学习自定义View(二)
- 一步一步实现Android自定义组合View
- 自定义UIPageControl 控件(二)
- 自定义控件(二)
- 自定义控件二
- Android自定义控件二
- 自定义控件(二)
- 自定义控件(二)
- 自定义控件二
- Android面试之Java引用类型简答
- Hadoop 集群监控
- 看,这个工具栏能伸缩折叠——Android CollapsingToolbarLayout使用介绍
- 被搞得晕头转向的LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR
- Android编译Release版本
- 一步一步实现自定义控件(二)
- Windows10系统下,彻底删除卸载MySQL
- 顺序表应用5:有序顺序表归并
- 比特币的技术——未来虚拟世界的地契
- SGU 101 - 120小结
- CodeForces 665B【暴力】
- # Executor源码分析
- [Leetcode] Is Subsequence
- 马尔科夫随机场(MRF)