一步一步实现自定义控件(二)

来源:互联网 发布:金山 阿里 腾讯 视频云 编辑:程序博客网 时间: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

3 0
原创粉丝点击